diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index e08c57a..ef4872f 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,12 +1,20 @@ name: Release Please on: - # Fires when a human merges the platform-managed release PR into main - # (the PR's base, so its diff shows the full pending release). Regenerations and - # custom-code pushes only touch scalar-next and never run this workflow. + # A release is cut when a human merges the platform-managed release PR into + # main (the PR's base, so its diff shows the full pending release). + # Every other push here runs release-please as well — commits written straight to + # this branch included — and cuts nothing, there being no release commit at the tip. push: branches: - main + # The Scalar platform pushes these workflow files straight to main to keep them + # current between releases; that is bookkeeping, never a release. Running anyway is worse + # than pointless on a branch that has not been handed a .release-please-manifest.json yet — + # release-please fails the run outright when it cannot read one. A release commit always + # rewrites the manifest and the changelog, so this filter can never swallow one. + paths-ignore: + - '.github/workflows/**' # Manual fallback only; nothing in the automated chain depends on dispatch. workflow_dispatch: @@ -35,7 +43,7 @@ jobs: # credential (so their CI runs without manual approval); this workflow only # cuts the tag + GitHub Release once a release PR is merged. skip-github-pull-request: true - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: ${{ steps.release.outputs.release_created == 'true' }} with: fetch-depth: 0 @@ -55,3 +63,46 @@ jobs: git checkout -B scalar-next origin/scalar-next git merge --no-edit -m "Sync release ${{ steps.release.outputs.tag_name }} back to scalar-next" "${{ steps.release.outputs.sha }}" git push origin scalar-next + publish: + needs: release-please + # `!cancelled()` keeps publishing even when the back-sync to the integration branch + # failed: a raced integration branch must not block the release from reaching its + # registry. + if: ${{ !cancelled() && needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + # Job-level permissions replace the workflow's release-cutting grants with the publish + # floor. OIDC trusted publishing verifies this run's claims, which name this workflow + # file — register release-please.yml as the trusted publisher on the registry. + permissions: + contents: read + id-token: write + packages: write + env: + VERSIONING_POLICY: manual + steps: + # Publishing runs inline — a top-level job of this same run — rather than dispatching + # sdk-release.yml: workflow_dispatch resolves the target workflow on the repository's + # default branch only, so a dispatch would 404 whenever the release line is any other + # branch, while OIDC trusted publishing accepts this job because it stays top-level + # (never a reusable-workflow call). The release-please job cut the tag earlier in this + # run; checking it out publishes exactly the released commit, not the branch head that + # triggered the workflow. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ needs.release-please.outputs.tag_name }} + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: stable + - run: cargo build --all-features + - uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 + id: crates-auth + - name: Publish to crates.io + run: | + VERSION="$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n1)" + if curl -sf -A "scalar-sdk-release" "https://crates.io/api/v1/crates/profound/${VERSION}" -o /dev/null; then + echo "profound@${VERSION} already on crates.io, skipping" + else + cargo publish + fi + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }} diff --git a/.github/workflows/release-title-edit.yml b/.github/workflows/release-title-edit.yml index d1827b1..c836f65 100644 --- a/.github/workflows/release-title-edit.yml +++ b/.github/workflows/release-title-edit.yml @@ -35,7 +35,7 @@ jobs: && startsWith(github.event.pull_request.title, 'release: ') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # The version committed on the PR head is what merging would actually release. ref: ${{ github.event.pull_request.head.sha }} @@ -108,7 +108,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # The PR head, so the version the pull request currently carries can be read before # deciding whether anything needs to change. diff --git a/.github/workflows/sdk-ci.yml b/.github/workflows/sdk-ci.yml index 3994a0e..d054c04 100644 --- a/.github/workflows/sdk-ci.yml +++ b/.github/workflows/sdk-ci.yml @@ -11,84 +11,23 @@ permissions: contents: read jobs: - # Lint gate: formatting plus clippy with warnings denied, over every target - # and feature — the same bar the generator holds its own baselines to, so a - # generated-code lint regression fails here instead of in a consumer's tree. - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable - with: - toolchain: stable - components: rustfmt, clippy - - run: cargo fmt --check - - run: cargo clippy --all-targets --all-features -- -D warnings - + # One verification job, matching every other target's generated CI: build the crate, then hold it + # to the toolchain's own checks. `--all-features` is load-bearing rather than cosmetic — the mock + # tests declare `required-features = ["mock", "tokio"]` so a bare `cargo test` would silently skip + # them, and the feature-gated surfaces (multipart, websocket) only compile — and only have their + # doctests collected — with their features on. verify: runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable with: toolchain: stable + components: rustfmt, clippy - run: cargo build --all-features - - # Runs the generated mock round-trips, the in-crate unit tests, and every - # doctest. `--all-features` is load-bearing rather than cosmetic: the mock - # tests declare `required-features = ["mock", "tokio"]` so a bare `cargo test` - # would silently skip them, and the feature-gated surfaces (multipart, - # websocket) only compile — and only have their doctests collected — with - # their features on. - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable - with: - toolchain: stable + - run: cargo clippy --all-targets --all-features -- -D warnings - run: cargo test --all-features - - # Proves the BYO-transport story: the library must compile with reqwest - # absent from the tree, and the hyper example (via --all-targets) must build - # against that reqwest-free surface. - no-default-features: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable - with: - toolchain: stable - - run: cargo check --no-default-features - - run: cargo check --no-default-features --features tokio --all-targets - - # Proves the MSRV floor declared as `rust-version` in Cargo.toml is real. - # `--locked` is the whole point: it forbids re-resolving, so the check runs - # against the exact versions the committed Cargo.lock pins. Without it a - # fresh resolve can pull a newer transitive dependency that raised its own - # floor, and the job would fail for a reason this crate never chose. The - # bootstrap line keeps the job green in a repo that has not committed a - # lockfile yet (the generator cannot emit one — it does not resolve - # dependencies); once Cargo.lock is committed the `test -f` short-circuits. - msrv: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable - with: - toolchain: 1.85.0 - - run: test -f Cargo.lock || cargo generate-lockfile - - run: cargo check --locked --all-targets --all-features - - # Dependency audit against `deny.toml`: RustSec advisories (vulnerable, - # unmaintained, unsound, yanked) and the license allow-list. Scoped to those - # two checks because they are the two `deny.toml` configures; `bans` and - # `sources` are left to the consumer to opt into. - deny: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1 - with: - command: check - command-arguments: advisories licenses + # Verification only, never auto-fix: the SDK is generated rustfmt-clean against the committed + # `rustfmt.toml`, so a diff here means either hand-edited custom code or a regenerate that was + # not committed. Auto-fixing would make the pushed bytes differ from generator output. + - run: cargo fmt --check diff --git a/.github/workflows/sdk-release.yml b/.github/workflows/sdk-release.yml new file mode 100644 index 0000000..b7fcea5 --- /dev/null +++ b/.github/workflows/sdk-release.yml @@ -0,0 +1,41 @@ +name: Rust SDK Release + +on: + # Manual re-publish only: the automated publish for each release runs as the `publish` job + # inside release-please.yml, in the same run that cuts the tag (dispatching a workflow here + # instead would resolve it on the repository's default branch only, which breaks releasing + # from any other release line). Dispatch this workflow at an existing release tag to re-run + # a failed or skipped publish. A dispatched run is top-level, so OIDC trusted-publisher + # claims name this file; register it as an additional trusted publisher only when manual + # re-publishes are used. Never add a workflow_call trigger here — registries reject + # publishes from called (reusable) workflows. + workflow_dispatch: + +permissions: + contents: read + id-token: write + packages: write + +jobs: + release: + runs-on: ubuntu-latest + env: + VERSIONING_POLICY: manual + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: stable + - run: cargo build --all-features + - uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 + id: crates-auth + - name: Publish to crates.io + run: | + VERSION="$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n1)" + if curl -sf -A "scalar-sdk-release" "https://crates.io/api/v1/crates/profound/${VERSION}" -o /dev/null; then + echo "profound@${VERSION} already on crates.io, skipping" + else + cargo publish + fi + env: + CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 466df71..a915e8c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.1.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index ff355ac..8cbda7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,94 @@ # Changelog +## [0.1.1](https://github.com/cooper-square-technologies/profound-rust-sdk/compare/v0.1.0...v0.1.1) (2026-09-25) + + +### ⚠ BREAKING CHANGES + +* **api:** 3 breaking changes to the SDK surface. + - Property `bot_provider_filter.value` type changed from `enum(openai | anthropic | chatgpt | …) | Array` to `enum(openai | anthropic | chatgpt | …) | Array`. + - Property `otf_intent_shares_query.platforms` type changed from `Array` to `Array`. + - Property `otf_volume_request.platforms` type changed from `Array` to `Array`. +* **api:** 3 breaking changes to the SDK surface. + - Property `bot_provider_filter.value` type changed from `enum(openai | anthropic | chatgpt | …) | Array` to `enum(openai | anthropic | chatgpt | …) | Array`. + - Property `sentiment_v2_query.metrics` type changed from `Array | null` to `Array | null`. + - Property `app_routes_v2_answer_engine_insights_reports_sentiment_sort_spec.field` type changed from `enum(occurrence | positive_sentiment | negative_sentiment)` to `enum(occurrence | positive_sentiment | negative_sentiment | …)`. +* **api:** 4 breaking changes to the SDK surface. + - Serialization or defaults of query param `order_by` on `organizations.categories.prompts` changed. + - Serialization or defaults of query param `order_dir` on `organizations.categories.prompts` changed. + - Serialization or defaults of query param `version` on `agents.retrieve` changed. + - Serialization or defaults of query param `version` on `agents.retrieveGraph` changed. +* **api:** 54 breaking changes to the SDK surface. + - Property `accuracy_breakdown_query.limit` type changed from `integer` to `integer`. + - Property `accuracy_breakdown_query.offset` type changed from `integer` to `integer`. + - Property `answers_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `answers_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `citations_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `citations_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `claim_citations_query.limit` type changed from `integer` to `integer`. + - Property `claim_citations_query.offset` type changed from `integer` to `integer`. + - Property `cluster_example_runs_query.limit` type changed from `integer` to `integer`. + - Property `cluster_example_runs_query.offset` type changed from `integer` to `integer`. + - Property `create_project_task_request.impact` type changed from `integer | null` to `integer | null`. + - Property `cursor_pagination.limit` type changed from `integer` to `integer`. + - Property `factcheck_claims_query.limit` type changed from `integer | null` to `integer | null`. + - Property `factcheck_claims_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `factcheck_scores_query.limit` type changed from `integer | null` to `integer | null`. + - Property `factcheck_scores_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `inaccuracy_drivers_query.limit` type changed from `integer` to `integer`. + - Property `inaccurate_clusters_query.limit` type changed from `integer` to `integer`. + - Property `inaccurate_clusters_query.offset` type changed from `integer` to `integer`. + - Property `inaccurate_themes_query.limit` type changed from `integer` to `integer`. + - Property `inaccurate_themes_query.offset` type changed from `integer` to `integer`. + - Property `pagination.limit` type changed from `integer` to `integer`. + - Property `pagination.offset` type changed from `integer` to `integer`. + - Property `project.task_count` type changed from `integer` to `integer`. + - Property `project.new_task_count` type changed from `integer` to `integer`. + - Property `project.version_count` type changed from `integer` to `integer`. + - Property `project_attachment.size_bytes` type changed from `integer` to `integer`. + - Property `project_detail.task_count` type changed from `integer` to `integer`. + - Property `project_detail.new_task_count` type changed from `integer` to `integer`. + - Property `project_detail.version_count` type changed from `integer` to `integer`. + - Property `project_list_item.task_count` type changed from `integer` to `integer`. + - Property `project_list_item.new_task_count` type changed from `integer` to `integer`. + - Property `project_task.impact` type changed from `integer | null` to `integer | null`. + - Property `project_task_detail.impact` type changed from `integer | null` to `integer | null`. + - Property `query_fanouts_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `query_fanouts_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `search_knowledge_base_request.top_k` type changed from `integer` to `integer`. + - Property `sentiment_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `sentiment_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `shopping_brands_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `shopping_brands_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `shopping_merchants_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `shopping_merchants_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `shopping_products_v2_query.competitor_limit` type changed from `integer` to `integer`. + - Property `shopping_products_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `shopping_products_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `shopping_trigger_rate_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `shopping_trigger_rate_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `top_inaccurate_claims_query.limit` type changed from `integer` to `integer`. + - Property `update_project_task_request.impact` type changed from `integer | null` to `integer | null`. + - Property `visibility_v2_query.limit` type changed from `integer | null` to `integer | null`. + - Property `visibility_v2_query.max_results` type changed from `integer | null` to `integer | null`. + - Property `youtube_channels_query.limit` type changed from `integer | null` to `integer | null`. + - Property `youtube_videos_query.limit` type changed from `integer | null` to `integer | null`. + +### Features + +* **api:** add operation promptVolumes.volume.onTheFly (+8 more changes) ([2b715a0](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/2b715a00988fa8b36d476cbaac378ae05cfe2688)) +* **api:** update property bot_provider_filter.value (+11 more changes) ([fc34ca8](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/fc34ca894124a5fcdc26acadef76ef64721d92a3)) +* **api:** update property bot_provider_filter.value (+21 more changes) ([9ee1600](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/9ee1600952d39f11fb6247bc284c8f76cd407861)) +* **api:** update SDK surface (5 changes) ([02d0d0a](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/02d0d0a9a4b577bcc8a269c62ddfebddc59055e6)) +* **api:** update SDK surface (55 changes) ([29c2089](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/29c2089349431aeab36f63866c3fd685abcb1908)) + + +### Chores + +* **api:** regenerate SDK ([01beceb](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/01beceb2bd802de6c298e4835acee989be9b7218)) +* release 0.1.1 ([729d5f4](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/729d5f4895f46b16e23f2fd2c86676fad5ca0ece)) +* release 0.1.1 ([7300c85](https://github.com/cooper-square-technologies/profound-rust-sdk/commit/7300c8531c187e82482f2a7996715fee0f5224b0)) + ## [0.1.0](https://github.com/cooper-square-technologies/profound-rust-sdk/compare/v0.0.1...v0.1.0) (2026-08-28) diff --git a/Cargo.toml b/Cargo.toml index 0d471b8..f9de0c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "profound" -version = "0.1.0" # x-release-please-version +version = "0.1.1" # x-release-please-version edition = "2024" rust-version = "1.85" description = "Rust library to access the Profound API" diff --git a/README.md b/README.md index 8e699b9..4e7f6f9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -profound = "0.1.0" # x-release-please-version +profound = "0.1.1" # x-release-please-version tokio = { version = "1", features = ["rt-multi-thread", "macros"] } ``` diff --git a/VERSIONING.md b/VERSIONING.md index 662f2b6..f9cf7dd 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -73,6 +73,10 @@ different fixes: platform and the `github-actions` bot to push (or be left unprotected); the default branch only ever advances by merging release PRs, and `scalar-next` receives each released state back from the release workflow. +- **`scalar-next` must not enable "Require linear history."** The Scalar platform merges each + regeneration into it, and merges a resolved conflict back the same way; the release sync + adds another merge whenever a release PR is squash- or rebase-merged. That setting rejects + all of them. - No Actions settings changes are required: the generated workflows declare their own permissions and never create pull requests. - If this package publishes through OIDC trusted publishing (for example PyPI or npm), @@ -84,3 +88,13 @@ different fixes: manual re-publishes at an existing tag; register it as an additional trusted publisher only if you use it. If the publish job is configured with a deployment environment, include that environment in the registration too. + +### Recommended, not required + +Releases work without this; it closes a window where one can go out at the wrong version. + +- **If this repository uses auto-merge, make the `Release PR version` check a required status + check.** Auto-merge merges as soon as the checks that are *required* go green, with no + human present — so a check that is not required but still red at that moment stops + nothing, which is exactly the window between retitling a release PR and the platform + re-rendering it. diff --git a/api.md b/api.md index 2c856a4..319e5aa 100644 --- a/api.md +++ b/api.md @@ -143,6 +143,11 @@ Complete reference of every operation, grouped by resource. See [the README](./R - [`Ads OpenaiAds`](#ads-openaiads) - [`Ads OpenaiAds AdAccount`](#ads-openaiads-adaccount) - [Get Account Insights](#get-account-insights) +- [`PromptVolumes`](#promptvolumes) + - [`PromptVolumes Volume`](#promptvolumes-volume) + - [Get Keyword Volume](#get-keyword-volume) + - [`PromptVolumes Intents`](#promptvolumes-intents) + - [Get Keyword Intent Shares](#get-keyword-intent-shares) ## Setup @@ -931,6 +936,7 @@ let mut events = client end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -1109,6 +1115,7 @@ let response = client end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -1473,6 +1480,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1508,6 +1516,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1591,6 +1600,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1629,6 +1639,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1667,6 +1678,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1701,6 +1713,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1735,6 +1748,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1769,6 +1783,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1807,6 +1822,7 @@ let response = client comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -2340,7 +2356,7 @@ let response = client "7c9e6679-7425-40de-944b-e07fc1f90ae7", SearchKnowledgeBaseRequest { query: "x".to_string(), - top_k: 0, + top_k: 1, return_full_page: None, filters: None, }, @@ -3026,3 +3042,67 @@ let response = client .send() .await?; ``` + +## `PromptVolumes` + +### `PromptVolumes Volume` + +#### Get Keyword Volume + +Weekly and monthly volume projections for one keyword. + +Each organization can look up 1,000 distinct keywords per UTC day; over +the cap returns 429 with Retry-After. Slices with two or fewer users are +omitted for privacy. + +| Direction | Type | +| --- | --- | +| Request | [`OtfVolumeRequest`](./src/models/volume.rs) | +| Response | [`OtfVolumeResponse`](./src/models/volume.rs) | + +```rust +let response = client + .prompt_volumes() + .volume() + .on_the_fly(OtfVolumeRequest { + keyword: "".to_string(), + matching_type: OtfVolumeRequestMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + organization_id: None, + }) + .send() + .await?; +``` + +### `PromptVolumes Intents` + +#### Get Keyword Intent Shares + +Intent shares for one keyword across the requested cohort weeks. + +Shares are fractions from 0 to 1. Cohorts with two or fewer matching users +are omitted for privacy. Doesn't use the daily keyword allowance. + +| Direction | Type | +| --- | --- | +| Request | [`OtfIntentSharesQuery`](./src/models/intents.rs) | +| Response | [`OtfIntentSharesResponse`](./src/models/intents.rs) | + +```rust +let response = client + .prompt_volumes() + .intents() + .on_the_fly(OtfIntentSharesQuery { + keyword: "".to_string(), + matching_type: OtfIntentSharesQueryMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + }) + .send() + .await?; +``` diff --git a/deny.toml b/deny.toml deleted file mode 100644 index 224a990..0000000 --- a/deny.toml +++ /dev/null @@ -1,25 +0,0 @@ -# Dependency policy for `cargo deny check advisories licenses` (see .github/workflows/sdk-ci.yml). - -[advisories] -# Vulnerability advisories are always errors. These three default to a softer level, so they -# are raised explicitly: an unmaintained or unsound dependency should block a release too. -unmaintained = "all" -unsound = "all" -yanked = "deny" -# Advisories triaged as not applicable go here, ideally with a `reason`: -# ignore = [{ id = "RUSTSEC-0000-0000", reason = "unreachable from this crate" }] -ignore = [] - -[licenses] -# Permissive-only allow-list. A dependency under any other license fails the check until it is -# reviewed and added here on purpose. -allow = [ - "MIT", - "Apache-2.0", - "BSD-2-Clause", - "BSD-3-Clause", - "ISC", - "Unicode-3.0", - "Zlib", - "CDLA-Permissive-2.0", -] diff --git a/examples/smoke-test.rs b/examples/smoke-test.rs index 93bf014..921d87a 100644 --- a/examples/smoke-test.rs +++ b/examples/smoke-test.rs @@ -480,7 +480,7 @@ async fn main() { .limit(10000) .cursor("example") .order_by(PromptOrderBy::CreatedAt) - .order_dir(OrderByDirection::Asc) + .order_dir(OrderByDirection::Desc) .analysis_type(vec![AnalysisType::Visibility]) .prompt_type(vec![PromptType::Visibility]) .status(vec!["active".to_string()]) @@ -1063,8 +1063,8 @@ async fn main() { end_date: "".to_string(), include: Some(vec![AnswersV2QueryInclude::RunId]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1144,8 +1144,8 @@ async fn main() { end_date: "".to_string(), include: Some(vec![AnswersV2QueryInclude::RunId]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2087,8 +2087,8 @@ async fn main() { interval: Some(CitationsV2QueryInterval::Day), scope: Some(CitationsV2QueryScope::All), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2181,8 +2181,8 @@ async fn main() { assets: None, filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsVisibilitySortSpec { field: None }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2224,6 +2224,7 @@ async fn main() { end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -2273,14 +2274,15 @@ async fn main() { end_date: "".to_string(), comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), + source: Some(SentimentV2QuerySource::Response), group_by: Some(vec![SentimentV2QueryGroupBy::Date]), metrics: Some(vec![SentimentV2QueryMetric::PositiveSentiment]), interval: Some(SentimentV2QueryInterval::Day), filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpec { field: None, dir: None }), include_cited_websites: Some(false), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2372,8 +2374,8 @@ async fn main() { field: "".to_string(), dir: None, }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2550,8 +2552,8 @@ async fn main() { assets: None, filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsVisibilitySortSpec { field: None }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2634,8 +2636,8 @@ async fn main() { interval: Some(CitationsV2QueryInterval::Day), scope: Some(CitationsV2QueryScope::All), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2673,6 +2675,7 @@ async fn main() { end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -2718,14 +2721,15 @@ async fn main() { end_date: "".to_string(), comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), + source: Some(SentimentV2QuerySource::Response), group_by: Some(vec![SentimentV2QueryGroupBy::Date]), metrics: Some(vec![SentimentV2QueryMetric::PositiveSentiment]), interval: Some(SentimentV2QueryInterval::Day), filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpec { field: None, dir: None }), include_cited_websites: Some(false), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2809,8 +2813,8 @@ async fn main() { field: "".to_string(), dir: None, }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3069,8 +3073,8 @@ async fn main() { scope: Some(ShoppingBrandsV2QueryScope::Owned), assets: None, filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3160,8 +3164,8 @@ async fn main() { scope: Some(ShoppingBrandsV2QueryScope::Owned), assets: None, filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3251,8 +3255,8 @@ async fn main() { target_product: Some("x".to_string()), competitor_limit: Some(5), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3344,8 +3348,8 @@ async fn main() { target_product: Some("x".to_string()), competitor_limit: Some(5), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3429,8 +3433,8 @@ async fn main() { metrics: Some(vec![ShoppingMerchantsV2QueryMetric::MerchantShare]), interval: Some(ShoppingMerchantsV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3516,8 +3520,8 @@ async fn main() { metrics: Some(vec![ShoppingMerchantsV2QueryMetric::MerchantShare]), interval: Some(ShoppingMerchantsV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3601,8 +3605,8 @@ async fn main() { metrics: Some(vec![ShoppingTriggerRateV2QueryMetric::TotalRuns]), interval: Some(ShoppingTriggerRateV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3688,8 +3692,8 @@ async fn main() { metrics: Some(vec![ShoppingTriggerRateV2QueryMetric::TotalRuns]), interval: Some(ShoppingTriggerRateV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3730,6 +3734,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3779,6 +3784,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3828,6 +3834,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3884,6 +3891,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4022,6 +4030,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4078,6 +4087,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4134,6 +4144,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4190,6 +4201,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4246,6 +4258,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4298,6 +4311,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4350,6 +4364,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4402,6 +4417,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4450,6 +4466,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4498,6 +4515,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4546,6 +4564,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4598,6 +4617,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4654,6 +4674,7 @@ async fn main() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -4708,6 +4729,7 @@ async fn main() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -4870,8 +4892,8 @@ async fn main() { end_date: "".to_string(), group_by: Some(vec![FactcheckScoresQueryGroupBy::Date]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -4953,8 +4975,8 @@ async fn main() { end_date: "".to_string(), group_by: Some(vec![FactcheckScoresQueryGroupBy::Date]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -5038,8 +5060,8 @@ async fn main() { group_by: Some(vec![FactcheckClaimsQueryGroupBy::Model]), filter: None, include: Some(vec![FactcheckClaimsQueryInclude::Theme]), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -5125,8 +5147,8 @@ async fn main() { group_by: Some(vec![FactcheckClaimsQueryGroupBy::Model]), filter: None, include: Some(vec![FactcheckClaimsQueryInclude::Theme]), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -5208,7 +5230,7 @@ async fn main() { start_date: "".to_string(), end_date: "".to_string(), filter: None, - limit: Some(0), + limit: Some(1), cursor: Some("".to_string()), source_types: Some(vec![YoutubeChannelsQuerySourceType::Video]), group_by: Some(vec![YoutubeChannelsQueryGroupBy::Channel]), @@ -5289,7 +5311,7 @@ async fn main() { start_date: "".to_string(), end_date: "".to_string(), filter: None, - limit: Some(0), + limit: Some(1), cursor: Some("".to_string()), source_types: Some(vec![YoutubeVideosQuerySourceType::Video]), attribution: Some(YoutubeVideosQueryAttribution::Attributed), @@ -5965,7 +5987,7 @@ async fn main() { "7c9e6679-7425-40de-944b-e07fc1f90ae7", SearchKnowledgeBaseRequest { query: "x".to_string(), - top_k: 0, + top_k: 1, return_full_page: None, filters: None, }, @@ -6002,7 +6024,7 @@ async fn main() { "7c9e6679-7425-40de-944b-e07fc1f90ae7", SearchKnowledgeBaseRequest { query: "x".to_string(), - top_k: 0, + top_k: 1, return_full_page: Some(false), filters: Some(SearchKnowledgeBaseFilters { tags: None, @@ -6504,7 +6526,7 @@ async fn main() { id: "x".to_string(), name: "x".to_string(), mime_type: "x".to_string(), - size_bytes: 0, + size_bytes: 1, data_base64: "x".to_string(), }]), generation_context: Some(ProjectGenerationContext { @@ -6886,7 +6908,7 @@ async fn main() { brief: Some("x".to_string()), r#type: Some("x".to_string()), topic: Some("x".to_string()), - impact: Some(0), + impact: Some(1), reference_url: Some("x".to_string()), reference_label: Some("x".to_string()), position: Some(0), @@ -7009,7 +7031,7 @@ async fn main() { brief: Some("x".to_string()), r#type: Some("x".to_string()), topic: Some("x".to_string()), - impact: Some(0), + impact: Some(1), reference_url: Some("x".to_string()), reference_label: Some("x".to_string()), }, @@ -7620,6 +7642,156 @@ async fn main() { error, }); } + if selected(&filter, "onTheFly", "/v2/prompt-volumes/volume/on-the-fly") { + let started = std::time::Instant::now(); + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .volume() + .on_the_fly(OtfVolumeRequest { + keyword: "".to_string(), + matching_type: OtfVolumeRequestMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + organization_id: None, + }) + .send() + .await?; + Ok(()) + } + .await; + let duration_ms = started.elapsed().as_millis() as i64; + let (status, error) = match result { + Ok(()) => ("passed", String::new()), + Err(error) if is_smoke_failure(&error) => ("failed", format!("{error}")), + // A response came back (API error or decode mismatch): the request + // reached the server, which is what this smoke verifies. + Err(_) => ("passed", String::new()), + }; + results.push(SmokeResult { + operation: "onTheFly".to_string(), + method: "POST".to_string(), + path: "/v2/prompt-volumes/volume/on-the-fly".to_string(), + label: "required params".to_string(), + status: status.to_string(), + duration_ms, + error, + }); + } + if selected(&filter, "onTheFly", "/v2/prompt-volumes/volume/on-the-fly") { + let started = std::time::Instant::now(); + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .volume() + .on_the_fly(OtfVolumeRequest { + keyword: "".to_string(), + matching_type: OtfVolumeRequestMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: Some(vec!["".to_string()]), + platforms: Some(vec![OtfVolumeRequestPlatform::ChatgptCom]), + organization_id: Some("7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()), + }) + .send() + .await?; + Ok(()) + } + .await; + let duration_ms = started.elapsed().as_millis() as i64; + let (status, error) = match result { + Ok(()) => ("passed", String::new()), + Err(error) if is_smoke_failure(&error) => ("failed", format!("{error}")), + // A response came back (API error or decode mismatch): the request + // reached the server, which is what this smoke verifies. + Err(_) => ("passed", String::new()), + }; + results.push(SmokeResult { + operation: "onTheFly".to_string(), + method: "POST".to_string(), + path: "/v2/prompt-volumes/volume/on-the-fly".to_string(), + label: "all params".to_string(), + status: status.to_string(), + duration_ms, + error, + }); + } + if selected(&filter, "onTheFly", "/v2/prompt-volumes/intents/on-the-fly") { + let started = std::time::Instant::now(); + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .intents() + .on_the_fly(OtfIntentSharesQuery { + keyword: "".to_string(), + matching_type: OtfIntentSharesQueryMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + }) + .send() + .await?; + Ok(()) + } + .await; + let duration_ms = started.elapsed().as_millis() as i64; + let (status, error) = match result { + Ok(()) => ("passed", String::new()), + Err(error) if is_smoke_failure(&error) => ("failed", format!("{error}")), + // A response came back (API error or decode mismatch): the request + // reached the server, which is what this smoke verifies. + Err(_) => ("passed", String::new()), + }; + results.push(SmokeResult { + operation: "onTheFly".to_string(), + method: "POST".to_string(), + path: "/v2/prompt-volumes/intents/on-the-fly".to_string(), + label: "required params".to_string(), + status: status.to_string(), + duration_ms, + error, + }); + } + if selected(&filter, "onTheFly", "/v2/prompt-volumes/intents/on-the-fly") { + let started = std::time::Instant::now(); + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .intents() + .on_the_fly(OtfIntentSharesQuery { + keyword: "".to_string(), + matching_type: OtfIntentSharesQueryMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: Some(vec!["".to_string()]), + platforms: Some(vec![OtfIntentSharesQueryPlatform::ChatgptCom]), + }) + .send() + .await?; + Ok(()) + } + .await; + let duration_ms = started.elapsed().as_millis() as i64; + let (status, error) = match result { + Ok(()) => ("passed", String::new()), + Err(error) if is_smoke_failure(&error) => ("failed", format!("{error}")), + // A response came back (API error or decode mismatch): the request + // reached the server, which is what this smoke verifies. + Err(_) => ("passed", String::new()), + }; + results.push(SmokeResult { + operation: "onTheFly".to_string(), + method: "POST".to_string(), + path: "/v2/prompt-volumes/intents/on-the-fly".to_string(), + label: "all params".to_string(), + status: status.to_string(), + duration_ms, + error, + }); + } if selected(&filter, "getBotsReportV2", "/v2/reports/bots") { results.push(SmokeResult { operation: "getBotsReportV2".to_string(), diff --git a/reference.md b/reference.md index 7e8359c..69ae480 100644 --- a/reference.md +++ b/reference.md @@ -983,6 +983,24 @@ Get Account Insights - Response body: `application/json` - Errors: `422` +### client.prompt_volumes().volume().on_the_fly(…) + +Get Keyword Volume + +- HTTP: `POST /v2/prompt-volumes/volume/on-the-fly` +- Request body: `application/json` +- Response body: `application/json` +- Errors: `422` + +### client.prompt_volumes().intents().on_the_fly(…) + +Get Keyword Intent Shares + +- HTTP: `POST /v2/prompt-volumes/intents/on-the-fly` +- Request body: `application/json` +- Response body: `application/json` +- Errors: `422` + ## Models - `AeoScore` @@ -1010,6 +1028,7 @@ Get Account Insights - `AgentDetail` - `AgentGraph` - `AgentRun` +- `AgentRunOutputDetail` - `AgentRunStatus` - `AgentRunStep` - `AgentSchema` @@ -1160,6 +1179,12 @@ Get Account Insights - `OrganizationAssetsResponse` - `OrganizationPersonasWithCategory` - `OrganizationPersonasWithCategoryResponse` +- `OtfIntentShareData` +- `OtfIntentSharesQuery` +- `OtfIntentSharesResponse` +- `OtfVolumeData` +- `OtfVolumeRequest` +- `OtfVolumeResponse` - `Pagination` - `PaginationInfo` - `PathFilter` @@ -1298,6 +1323,7 @@ Get Account Insights - `VisibilityV2Info` - `VisibilityV2Query` - `VisibilityV2Response` +- `VolumeResponseInfo` - `WebSearchResultsQuery` - `WebSearchResultsResponse` - `WebSearchResultsResult` diff --git a/scalar-sdk.manifest.json b/scalar-sdk.manifest.json index fdcff2d..4c8f4e7 100644 --- a/scalar-sdk.manifest.json +++ b/scalar-sdk.manifest.json @@ -1,7 +1,7 @@ { "name": "Profound", "slug": "profound", - "generatorVersion": "0.30.1", + "generatorVersion": "0.33.3", "servers": [ "https://api.tryprofound.com" ], @@ -234,6 +234,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -553,7 +574,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1 + } } }, { @@ -564,7 +587,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -1007,6 +1032,16 @@ "type": "string" } }, + { + "name": "markdownContentTruncated", + "publicName": "markdownContentTruncated", + "required": false, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "boolean" + } + }, { "name": "claims", "publicName": "claims", @@ -1287,6 +1322,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -2492,6 +2548,31 @@ } } }, + { + "name": "outputs_expanded", + "publicName": "outputs_expanded", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "record", + "value": { + "kind": "ref", + "name": "agent_run_output_detail" + }, + "propertyNames": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "steps", "publicName": "steps", @@ -2517,6 +2598,45 @@ "additionalProperties": false } }, + { + "name": "agent_run_output_detail", + "source": "agent_run_output_detail", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "title", + "publicName": "title", + "required": true, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "null" + } + ] + } + }, + { + "name": "value", + "publicName": "value", + "required": true, + "deprecated": false, + "type": { + "kind": "unknown" + } + } + ], + "additionalProperties": false + } + }, { "name": "agent_run_status", "source": "agent_run_status", @@ -4522,7 +4642,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 200, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -4540,7 +4663,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -4939,7 +5065,6 @@ "duckduckgo", "you", "you.com", - "xai", "grok", "gemini", "mistral", @@ -4948,7 +5073,8 @@ "baidu", "yahoo", "commoncrawl", - "openclaw" + "openclaw", + "exa" ], "names": [ "Openai", @@ -4965,7 +5091,6 @@ "Duckduckgo", "You", "YouCom", - "Xai", "Grok", "Gemini", "Mistral", @@ -4974,7 +5099,8 @@ "Baidu", "Yahoo", "Commoncrawl", - "Openclaw" + "Openclaw", + "Exa" ], "deprecations": [ false, @@ -5022,7 +5148,6 @@ "duckduckgo", "you", "you.com", - "xai", "grok", "gemini", "mistral", @@ -5031,7 +5156,8 @@ "baidu", "yahoo", "commoncrawl", - "openclaw" + "openclaw", + "exa" ], "names": [ "Openai", @@ -5048,7 +5174,6 @@ "Duckduckgo", "You", "YouCom", - "Xai", "Grok", "Gemini", "Mistral", @@ -5057,7 +5182,8 @@ "Baidu", "Yahoo", "Commoncrawl", - "Openclaw" + "Openclaw", + "Exa" ], "deprecations": [ false, @@ -7821,7 +7947,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -7839,7 +7968,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -7979,6 +8111,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -8641,6 +8794,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -8864,7 +9038,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1 + } } }, { @@ -8875,7 +9051,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -9393,6 +9571,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -9616,7 +9815,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1 + } } }, { @@ -9627,7 +9828,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } } ], @@ -10793,7 +10996,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 5 + } }, { "kind": "null" @@ -11178,7 +11384,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } } }, { @@ -12688,7 +12897,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 100, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -12706,7 +12918,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -13625,7 +13840,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 100, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -13643,7 +13861,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -14255,6 +14476,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -14467,7 +14709,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 50 + } } } ], @@ -14748,6 +14993,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -14979,7 +15245,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 10000 + } } }, { @@ -14990,7 +15259,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -15265,6 +15536,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -15477,7 +15769,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1 + } } }, { @@ -15488,7 +15782,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -17459,6 +17755,428 @@ "additionalProperties": false } }, + { + "name": "otf_intent_share_data", + "source": "otf_intent_share_data", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "main_classification", + "publicName": "main_classification", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "sub_category_classification", + "publicName": "sub_category_classification", + "required": true, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "null" + } + ] + } + }, + { + "name": "pct_conversation", + "publicName": "pct_conversation", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "number" + } + } + ], + "additionalProperties": false + } + }, + { + "name": "otf_intent_shares_query", + "source": "otf_intent_shares_query", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "keyword", + "publicName": "keyword", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "matching_type", + "publicName": "matching_type", + "required": true, + "deprecated": false, + "type": { + "kind": "enum", + "values": [ + "exact_match", + "phrase_match" + ], + "names": [ + "ExactMatch", + "PhraseMatch" + ], + "deprecations": [ + false, + false + ] + } + }, + { + "name": "start_date", + "publicName": "start_date", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string", + "format": "date" + } + }, + { + "name": "end_date", + "publicName": "end_date", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string", + "format": "date" + } + }, + { + "name": "regions", + "publicName": "regions", + "required": false, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + } + }, + { + "name": "platforms", + "publicName": "platforms", + "required": false, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "enum", + "values": [ + "chatgpt.com", + "gemini.google.com", + "perplexity.ai" + ], + "names": [ + "ChatgptCom", + "GeminiGoogleCom", + "PerplexityAi" + ], + "deprecations": [ + false, + false, + false + ] + } + } + } + ], + "additionalProperties": false + } + }, + { + "name": "otf_intent_shares_response", + "source": "otf_intent_shares_response", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "info", + "publicName": "info", + "required": true, + "deprecated": false, + "type": { + "kind": "ref", + "name": "volume_response_info" + } + }, + { + "name": "data", + "publicName": "data", + "required": true, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "ref", + "name": "otf_intent_share_data" + } + } + } + ], + "additionalProperties": false + } + }, + { + "name": "otf_volume_data", + "source": "otf_volume_data", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "country_code", + "publicName": "country_code", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "platform", + "publicName": "platform", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "matching_type", + "publicName": "matching_type", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "frequency", + "publicName": "frequency", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "date", + "publicName": "date", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string", + "format": "date" + } + }, + { + "name": "volume", + "publicName": "volume", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "number" + } + } + ], + "additionalProperties": false + } + }, + { + "name": "otf_volume_request", + "source": "otf_volume_request", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "keyword", + "publicName": "keyword", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string" + } + }, + { + "name": "matching_type", + "publicName": "matching_type", + "required": true, + "deprecated": false, + "type": { + "kind": "enum", + "values": [ + "exact_match", + "phrase_match" + ], + "names": [ + "ExactMatch", + "PhraseMatch" + ], + "deprecations": [ + false, + false + ] + } + }, + { + "name": "start_date", + "publicName": "start_date", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string", + "format": "date" + } + }, + { + "name": "end_date", + "publicName": "end_date", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "string", + "format": "date" + } + }, + { + "name": "regions", + "publicName": "regions", + "required": false, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + } + }, + { + "name": "platforms", + "publicName": "platforms", + "required": false, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "enum", + "values": [ + "chatgpt.com", + "gemini.google.com", + "perplexity.ai" + ], + "names": [ + "ChatgptCom", + "GeminiGoogleCom", + "PerplexityAi" + ], + "deprecations": [ + false, + false, + false + ] + } + } + }, + { + "name": "organization_id", + "publicName": "organization_id", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "string", + "format": "uuid" + }, + { + "kind": "null" + } + ] + } + } + ], + "additionalProperties": false + } + }, + { + "name": "otf_volume_response", + "source": "otf_volume_response", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "info", + "publicName": "info", + "required": true, + "deprecated": false, + "type": { + "kind": "ref", + "name": "volume_response_info" + } + }, + { + "name": "data", + "publicName": "data", + "required": true, + "deprecated": false, + "type": { + "kind": "array", + "items": { + "kind": "ref", + "name": "otf_volume_data" + } + } + } + ], + "additionalProperties": false + } + }, { "name": "pagination", "source": "pagination", @@ -17475,7 +18193,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } } }, { @@ -17486,7 +18207,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } } ], @@ -18210,7 +18933,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -18221,7 +18946,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -18288,7 +19015,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -18407,7 +19136,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 10485760, + "exclusiveMinimum": 0 + } } }, { @@ -18659,7 +19391,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -18670,7 +19404,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -18737,7 +19473,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -19529,7 +20267,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -19540,7 +20280,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 0 + } } }, { @@ -19875,7 +20617,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 5 + } }, { "kind": "null" @@ -20132,7 +20877,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 5 + } }, { "kind": "null" @@ -22108,7 +22856,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -22126,7 +22877,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -24105,7 +24859,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 100 + } } }, { @@ -24291,6 +25048,24 @@ } ] } + }, + { + "name": "citation_share", + "publicName": "citation_share", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "null" + } + ] + } } ], "additionalProperties": { @@ -24860,6 +25635,24 @@ ] } }, + { + "name": "page", + "publicName": "page", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "string" + }, + { + "kind": "null" + } + ] + } + }, { "name": "positive_sentiment", "publicName": "positive_sentiment", @@ -24914,6 +25707,24 @@ ] } }, + { + "name": "citation_share", + "publicName": "citation_share", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "primitive", + "type": "number" + }, + { + "kind": "null" + } + ] + } + }, { "name": "previous", "publicName": "previous", @@ -25792,6 +26603,27 @@ ] } }, + { + "name": "source", + "publicName": "source", + "required": false, + "deprecated": false, + "type": { + "kind": "enum", + "values": [ + "response", + "citation" + ], + "names": [ + "Response", + "Citation" + ], + "deprecations": [ + false, + false + ] + } + }, { "name": "group_by", "publicName": "group_by", @@ -25858,14 +26690,17 @@ "values": [ "positive_sentiment", "negative_sentiment", - "occurrence" + "occurrence", + "citation_share" ], "names": [ "PositiveSentiment", "NegativeSentiment", - "Occurrence" + "Occurrence", + "CitationShare" ], "deprecations": [ + false, false, false, false @@ -25951,7 +26786,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -25969,7 +26807,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -28037,7 +28878,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -28056,7 +28900,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -28660,7 +29507,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -28679,7 +29529,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -29377,7 +30230,9 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1 + } } }, { @@ -29409,7 +30264,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -29428,7 +30286,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -29938,7 +30799,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -29957,7 +30821,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50000, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -31762,6 +32629,27 @@ "format": "uuid" } }, + { + "name": "languages", + "publicName": "languages", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "array", + "items": { + "kind": "primitive", + "type": "string" + } + }, + { + "kind": "null" + } + ] + } + }, { "name": "topic_ids", "publicName": "topic_ids", @@ -31974,7 +32862,10 @@ "type": { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 50 + } } } ], @@ -32499,7 +33390,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "minimum": 1, + "maximum": 5 + } }, { "kind": "null" @@ -34185,7 +35079,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -34203,7 +35100,10 @@ "variants": [ { "kind": "primitive", - "type": "integer" + "type": "integer", + "validation": { + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -34268,6 +35168,59 @@ "additionalProperties": false } }, + { + "name": "volume_response_info", + "source": "volume_response_info", + "publicAliases": [], + "deprecated": false, + "type": { + "kind": "object", + "properties": [ + { + "name": "total_rows", + "publicName": "total_rows", + "required": true, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "integer" + } + }, + { + "name": "truncated", + "publicName": "truncated", + "required": false, + "deprecated": false, + "type": { + "kind": "primitive", + "type": "boolean" + } + }, + { + "name": "query", + "publicName": "query", + "required": false, + "deprecated": false, + "type": { + "kind": "union", + "variants": [ + { + "kind": "object", + "properties": [], + "additionalProperties": { + "kind": "unknown" + } + }, + { + "kind": "null" + } + ] + } + } + ], + "additionalProperties": false + } + }, { "name": "web_search_results_query", "source": "web_search_results_query", @@ -35217,7 +36170,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -36264,7 +37220,10 @@ { "kind": "primitive", "type": "integer", - "validation": {} + "validation": { + "maximum": 50, + "exclusiveMinimum": 0 + } }, { "kind": "null" @@ -36455,14 +37414,17 @@ "values": [ "occurrence", "positive_sentiment", - "negative_sentiment" + "negative_sentiment", + "citation_share" ], "names": [ "Occurrence", "PositiveSentiment", - "NegativeSentiment" + "NegativeSentiment", + "CitationShare" ], "deprecations": [ + false, false, false, false @@ -36648,7 +37610,10 @@ "documents", "ads", "ads.openaiAds", - "ads.openaiAds.adAccount" + "ads.openaiAds.adAccount", + "promptVolumes", + "promptVolumes.volume", + "promptVolumes.intents" ], "publicResources": [ "organizations", @@ -36677,7 +37642,10 @@ "documents", "ads", "ads.openaiAds", - "ads.openaiAds.adAccount" + "ads.openaiAds.adAccount", + "promptVolumes", + "promptVolumes.volume", + "promptVolumes.intents" ], "operations": [ { @@ -37399,13 +38367,15 @@ "name": "order_by", "required": false, "style": "form", - "explode": true + "explode": true, + "defaultValue": "created_at" }, { "name": "order_dir", "required": false, "style": "form", - "explode": true + "explode": true, + "defaultValue": "desc" }, { "name": "analysis_type", @@ -40247,6 +41217,7 @@ "end_date", "comparison_start_date", "comparison_end_date", + "source", "group_by", "metrics", "interval", @@ -40264,6 +41235,7 @@ "end_date", "comparison_start_date", "comparison_end_date", + "source", "group_by", "metrics", "interval", @@ -40299,6 +41271,10 @@ "name": "comparison_end_date", "required": false }, + { + "name": "source", + "required": false + }, { "name": "group_by", "required": false @@ -41213,6 +42189,7 @@ "end_date", "comparison_start_date", "comparison_end_date", + "source", "group_by", "metrics", "interval", @@ -41230,6 +42207,7 @@ "end_date", "comparison_start_date", "comparison_end_date", + "source", "group_by", "metrics", "interval", @@ -41265,6 +42243,10 @@ "name": "comparison_end_date", "required": false }, + { + "name": "source", + "required": false + }, { "name": "group_by", "required": false @@ -42997,6 +43979,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43017,6 +44000,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43052,6 +44036,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -43188,6 +44176,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43215,6 +44204,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43257,6 +44247,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -43625,6 +44619,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43648,6 +44643,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43686,6 +44682,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -43834,6 +44834,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43857,6 +44858,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -43895,6 +44897,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -44043,6 +45049,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44062,6 +45069,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44096,6 +45104,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -44228,6 +45240,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44247,6 +45260,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44281,6 +45295,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -44413,6 +45431,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44432,6 +45451,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44466,6 +45486,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -44598,6 +45622,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44621,6 +45646,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44659,6 +45685,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -44807,6 +45837,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44828,6 +45859,7 @@ "comparison_start_date", "comparison_end_date", "category_id", + "languages", "topic_ids", "exclude_topic_ids", "tag_ids", @@ -44864,6 +45896,10 @@ "name": "category_id", "required": true }, + { + "name": "languages", + "required": false + }, { "name": "topic_ids", "required": false @@ -46391,7 +47427,8 @@ "name": "version", "required": false, "style": "form", - "explode": true + "explode": true, + "defaultValue": "published" } ], "headerParamDetails": [], @@ -46765,7 +47802,8 @@ "name": "version", "required": false, "style": "form", - "explode": true + "explode": true, + "defaultValue": "published" } ], "headerParamDetails": [], @@ -50394,6 +51432,250 @@ ], "responseLinks": [], "transport": "http" + }, + { + "resource": "promptVolumes.volume", + "publicResource": "promptVolumes.volume", + "operation": "onTheFly", + "publicOperation": "onTheFly", + "deprecated": false, + "method": "POST", + "path": "/v2/prompt-volumes/volume/on-the-fly", + "pathParams": [], + "publicPathParams": [], + "queryParams": [], + "publicQueryParams": [], + "headerParams": [], + "publicHeaderParams": [], + "bodyParams": [ + "keyword", + "matching_type", + "start_date", + "end_date", + "regions", + "platforms", + "organization_id" + ], + "publicBodyParams": [ + "keyword", + "matching_type", + "start_date", + "end_date", + "regions", + "platforms", + "organization_id" + ], + "bodyParamDetails": [ + { + "name": "keyword", + "required": true + }, + { + "name": "matching_type", + "required": true + }, + { + "name": "start_date", + "required": true + }, + { + "name": "end_date", + "required": true + }, + { + "name": "regions", + "required": false + }, + { + "name": "platforms", + "required": false + }, + { + "name": "organization_id", + "required": false + } + ], + "pathParamDetails": [], + "queryParamDetails": [], + "headerParamDetails": [], + "cookieParams": [], + "publicCookieParams": [], + "cookieParamDetails": [], + "paramsModel": { + "publicName": "VolumeOnTheFlyParams" + }, + "requestBody": { + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ], + "required": true, + "publicName": "body", + "publicIdentifier": "body" + }, + "response": { + "status": "200", + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ] + }, + "responseModel": { + "name": "otf_volume_response", + "publicAliases": [] + }, + "result": { + "successStatus": "200", + "errorStatuses": [ + "422" + ] + }, + "errorResponses": [ + { + "status": "422", + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ], + "model": { + "name": "http_validation_error", + "publicAliases": [] + } + } + ], + "responseLinks": [], + "transport": "http" + }, + { + "resource": "promptVolumes.intents", + "publicResource": "promptVolumes.intents", + "operation": "onTheFly", + "publicOperation": "onTheFly", + "deprecated": false, + "method": "POST", + "path": "/v2/prompt-volumes/intents/on-the-fly", + "pathParams": [], + "publicPathParams": [], + "queryParams": [], + "publicQueryParams": [], + "headerParams": [], + "publicHeaderParams": [], + "bodyParams": [ + "keyword", + "matching_type", + "start_date", + "end_date", + "regions", + "platforms" + ], + "publicBodyParams": [ + "keyword", + "matching_type", + "start_date", + "end_date", + "regions", + "platforms" + ], + "bodyParamDetails": [ + { + "name": "keyword", + "required": true + }, + { + "name": "matching_type", + "required": true + }, + { + "name": "start_date", + "required": true + }, + { + "name": "end_date", + "required": true + }, + { + "name": "regions", + "required": false + }, + { + "name": "platforms", + "required": false + } + ], + "pathParamDetails": [], + "queryParamDetails": [], + "headerParamDetails": [], + "cookieParams": [], + "publicCookieParams": [], + "cookieParamDetails": [], + "paramsModel": { + "publicName": "IntentOnTheFlyParams" + }, + "requestBody": { + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ], + "required": true, + "publicName": "body", + "publicIdentifier": "body" + }, + "response": { + "status": "200", + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ] + }, + "responseModel": { + "name": "otf_intent_shares_response", + "publicAliases": [] + }, + "result": { + "successStatus": "200", + "errorStatuses": [ + "422" + ] + }, + "errorResponses": [ + { + "status": "422", + "contentType": "application/json", + "encoding": "json", + "contents": [ + { + "contentType": "application/json", + "encoding": "json" + } + ], + "model": { + "name": "http_validation_error", + "publicAliases": [] + } + } + ], + "responseLinks": [], + "transport": "http" } ], "webhooks": [] diff --git a/src/datetime.rs b/src/datetime.rs new file mode 100644 index 0000000..2d50abe --- /dev/null +++ b/src/datetime.rs @@ -0,0 +1,258 @@ +//! Lenient deserialization for `format: date-time` values. +//! +//! chrono's stock `Deserialize` accepts only full RFC 3339, which rejects a +//! large share of real API output: .NET and Postgres-backed services routinely +//! emit `2025-12-01T09:50:03.559` or `2025-08-25T20:00:00` with no offset at +//! all, and one such field fails the whole response body. +//! +//! Reads try RFC 3339 first, then the narrow-offset spellings RFC 3339 rejects, +//! then the offset-less ones — assuming UTC only when the input carries no +//! offset of its own. Serialization is deliberately untouched: chrono's own +//! `Serialize` already round-trips a `-04:00` back as `-04:00` and re-renders +//! `Z` as `Z`, which is exactly what the server sent, so these helpers only ever +//! supply `deserialize_with`. + +use chrono::TimeZone as _; +use serde::Deserialize as _; + +/// The date-time type this crate's models use, per the emitter's `date_time_type`. +type Target = chrono::DateTime; + +/// Offset-bearing formats accepted after RFC 3339 fails, in order. +/// +/// RFC 3339 requires a full `+HH:MM` offset, so `parse_from_rfc3339` rejects the +/// `+00` and `+0000` spellings of the very same instant — and `+00` is what +/// `psql` prints for a `timestamptz` by default. `%#z` accepts all three widths. +/// +/// These sit above the offset-less formats on purpose: `%#z` still requires an +/// offset to be present, so an offset-less input falls past them to the +/// UTC-assumption path rather than being silently matched here. +const OFFSET_FORMATS: &[&str] = &["%Y-%m-%dT%H:%M:%S%.f%#z", "%Y-%m-%d %H:%M:%S%.f%#z"]; + +/// Offset-less formats accepted after every offset-bearing attempt fails, in order. +/// +/// `%.f` matches an optional fractional-second part, so one entry covers both +/// `…:03.559` and `…:03`. The space-separated spelling is what Postgres and +/// several ORMs write when the column carries no zone. +const NAIVE_FORMATS: &[&str] = &["%Y-%m-%dT%H:%M:%S%.f", "%Y-%m-%d %H:%M:%S%.f"]; + +/// Parses one timestamp, falling back through the offset-bearing formats and +/// then the offset-less ones. +/// +/// An offset-less input is read as UTC — the only defensible reading when the +/// server declined to say, and the one that keeps the value stable across the +/// caller's local timezone. An input that does carry an offset never reaches +/// that path, so nothing is ever guessed over a stated zone. +fn parse(value: &str) -> Result { + if let Ok(parsed) = chrono::DateTime::parse_from_rfc3339(value) { + return Ok(convert(parsed)); + } + for format in OFFSET_FORMATS { + if let Ok(parsed) = chrono::DateTime::parse_from_str(value, format) { + return Ok(convert(parsed)); + } + } + for format in NAIVE_FORMATS { + if let Ok(naive) = chrono::NaiveDateTime::parse_from_str(value, format) { + return Ok(convert(chrono::Utc.from_utc_datetime(&naive).fixed_offset())); + } + } + Err(format!( + "expected an RFC 3339 date-time (or `YYYY-MM-DDTHH:MM:SS[.fff][±HH[:MM]]`), got {value:?}" + )) +} + +/// Narrows a parsed fixed-offset timestamp to the crate's configured target type. +fn convert(value: chrono::DateTime) -> Target { + value +} + +/// Deserialization shim: the one place the lenient parse is wired into serde. +/// +/// Every public entry point below routes through it so a container (`Option`, +/// `Vec`, `HashMap`) can reuse serde's own impl for the container itself and +/// override only the element. +struct Flexible(Target); + +impl<'de> serde::Deserialize<'de> for Flexible { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_str(FlexibleVisitor) + } +} + +/// Parses straight out of the deserializer's own buffer. +/// +/// Deserializing a `String` first would heap-allocate once per timestamp — +/// including on the common RFC 3339 path, where nothing lenient is needed — and +/// a paginated page of date-time fields pays that per row. A visitor sees the +/// borrowed `&str` instead, which is what chrono's own stock impl does. A +/// `Cow<'de, str>` would not have helped: serde's blanket impl for `Cow` always +/// yields `Cow::Owned`. +struct FlexibleVisitor; + +impl serde::de::Visitor<'_> for FlexibleVisitor { + type Value = Flexible; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("an RFC 3339 date-time string") + } + + fn visit_str(self, value: &str) -> Result { + parse(value).map(Flexible).map_err(serde::de::Error::custom) + } +} + +/// `#[serde(deserialize_with = "crate::datetime::deserialize")]` for a bare field. +pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result { + Flexible::deserialize(deserializer).map(|value| value.0) +} + +/// The `Option<_>` field shape. Absent and `null` both stay `None`. +pub mod option { + use serde::Deserialize as _; + + /// `#[serde(deserialize_with = "crate::datetime::option::deserialize")]`. + pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + Ok(Option::::deserialize(deserializer)?.map(|value| value.0)) + } +} + +/// The `Vec<_>` field shape. +pub mod vec { + use serde::Deserialize as _; + + /// `#[serde(deserialize_with = "crate::datetime::vec::deserialize")]`. + pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + Ok(Vec::::deserialize(deserializer)? + .into_iter() + .map(|value| value.0) + .collect()) + } +} + +/// The `Option>` field shape. +pub mod option_vec { + use serde::Deserialize as _; + + /// `#[serde(deserialize_with = "crate::datetime::option_vec::deserialize")]`. + pub fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + Ok(Option::>::deserialize(deserializer)? + .map(|values| values.into_iter().map(|value| value.0).collect())) + } +} + +/// The `HashMap` field shape. +pub mod map { + use serde::Deserialize as _; + + /// `#[serde(deserialize_with = "crate::datetime::map::deserialize")]`. + pub fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + Ok( + std::collections::HashMap::::deserialize(deserializer)? + .into_iter() + .map(|(key, value)| (key, value.0)) + .collect(), + ) + } +} + +/// The `Option>` field shape. +pub mod option_map { + use serde::Deserialize as _; + + /// `#[serde(deserialize_with = "crate::datetime::option_map::deserialize")]`. + pub fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result>, D::Error> { + Ok( + Option::>::deserialize(deserializer)? + .map(|values| values.into_iter().map(|(key, value)| (key, value.0)).collect()), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, serde::Serialize, serde::Deserialize)] + struct Wrapper { + #[serde(deserialize_with = "crate::datetime::deserialize")] + at: Target, + } + + #[derive(Debug, serde::Serialize, serde::Deserialize)] + struct OptionalWrapper { + #[serde(default, deserialize_with = "crate::datetime::option::deserialize")] + at: Option, + } + + fn decode(raw: &str) -> Target { + serde_json::from_str::(&format!(r#"{{"at":"{raw}"}}"#)) + .expect("decodes") + .at + } + + #[test] + fn accepts_rfc_3339_with_and_without_fractional_seconds() { + assert_eq!(decode("2025-12-01T09:50:03.559Z").timestamp(), 1764582603); + assert_eq!(decode("2025-12-01T09:50:03Z").timestamp(), 1764582603); + } + + #[test] + fn accepts_offset_less_timestamps_as_utc() { + // The .NET/Postgres shape that fails the whole response body against + // chrono's stock impl. + assert_eq!(decode("2025-12-01T09:50:03.559").timestamp(), 1764582603); + assert_eq!(decode("2025-08-25T20:00:00").timestamp(), 1756152000); + assert_eq!(decode("2025-08-25 20:00:00").timestamp(), 1756152000); + } + + #[test] + fn accepts_the_narrow_offsets_rfc_3339_rejects() { + // `2025-12-01 09:50:03+00` is `psql`'s default `timestamptz` rendering; + // RFC 3339 wants `+00:00` and rejects both narrower spellings outright. + assert_eq!(decode("2025-12-01 09:50:03+00").timestamp(), 1764582603); + assert_eq!(decode("2025-12-01 09:50:03.559+00").timestamp(), 1764582603); + assert_eq!(decode("2025-12-01T09:50:03+0000").timestamp(), 1764582603); + // A stated non-UTC offset is honored, never re-read as UTC. + assert_eq!(decode("2025-12-01T09:50:03-0400").timestamp(), 1764597003); + } + + #[test] + fn reports_the_offending_value_when_nothing_parses() { + let error = serde_json::from_str::(r#"{"at":"not-a-date"}"#) + .expect_err("rejects") + .to_string(); + assert!(error.contains("not-a-date"), "{error}"); + } + + #[test] + fn optional_fields_treat_absent_and_null_alike() { + assert!( + serde_json::from_str::("{}") + .expect("decodes") + .at + .is_none() + ); + assert!( + serde_json::from_str::(r#"{"at":null}"#) + .expect("decodes") + .at + .is_none() + ); + } + + #[test] + fn serialization_is_left_to_chrono() { + // The offset the server sent has to survive the round trip; rewriting + // every `-04:00` to `+00:00` loses information for no benefit. + let wrapper: Wrapper = serde_json::from_str(r#"{"at":"2024-11-16T08:15:33.4364067-04:00"}"#).expect("decodes"); + let encoded = serde_json::to_string(&wrapper).expect("encodes"); + assert!(encoded.contains("-04:00"), "{encoded}"); + } +} diff --git a/src/http.rs b/src/http.rs index bad8d2e..3be03ba 100644 --- a/src/http.rs +++ b/src/http.rs @@ -782,6 +782,35 @@ pub fn scalar_value(value: &T) -> String { match serde_json::to_value(value) { Ok(serde_json::Value::String(text)) => text, Ok(serde_json::Value::Null) => String::new(), + // A `type: number` param is an `f64`, so `to_string` renders `limit=100.0` + // where the document means `limit=100`. Model fields get the same + // treatment through `crate::number`, but a builder param never passes + // through a model, so the rule is applied here too. Inlined rather than + // shared with `crate::number`: that module is capability-gated and this + // one is unconditional core runtime. + Ok(serde_json::Value::Number(number)) => { + // An `integer` param already holds the exact value in `i64`/`u64`, so + // it is rendered straight from there. Routing it through `f64` first + // would round it: `9007199254740993` (2^53 + 1) is not representable, + // and the nearest `f64` is 2^53 — which still passes the "is this an + // exact integer" guard below and would silently ship an off-by-one. + if let Some(integer) = number.as_i64() { + integer.to_string() + } else if let Some(unsigned) = number.as_u64() { + unsigned.to_string() + } else { + // Only a genuine `f64` reaches here, so the reshape below cannot + // lose an integer that a wider type was holding exactly. + match number.as_f64() { + Some(float) + if float.is_finite() && float.fract() == 0.0 && float.abs() <= 9_007_199_254_740_992.0 => + { + (float as i64).to_string() + } + _ => number.to_string(), + } + } + } Ok(other) => other.to_string(), Err(_) => String::new(), } @@ -1052,6 +1081,49 @@ mod tests { assert_eq!(paging_count(&f64::NAN), None); } + #[test] + fn scalar_value_renders_integral_numbers_without_a_fractional_part() { + // A `type: number` param is an `f64`, and serde's own rendering would put + // `limit=100.0` on the wire where the document means `limit=100`. + assert_eq!(scalar_value(&100.0_f64), "100"); + assert_eq!(scalar_value(&-100.0_f64), "-100"); + // JSON has no signed zero to preserve. + assert_eq!(scalar_value(&-0.0_f64), "0"); + // A genuinely fractional value keeps every digit. + assert_eq!(scalar_value(&1.5_f64), "1.5"); + assert_eq!(scalar_value(&0.1_f64), "0.1"); + // Past f64's exact-integer range an `as i64` cast stops describing the + // value the float actually holds, so the float rendering stands. + assert_eq!(scalar_value(&1e300_f64), serde_json::json!(1e300_f64).to_string()); + // serde_json cannot represent a non-finite number, so it arrives as null + // and drops out — the same thing it did before integral rendering existed. + assert_eq!(scalar_value(&f64::NAN), ""); + assert_eq!(scalar_value(&f64::INFINITY), ""); + } + + #[test] + fn scalar_value_keeps_full_precision_for_large_integer_params() { + // An `integer` param is read from `i64`/`u64` directly. Routing it through + // `f64` first silently rounds it: 2^53 + 1 has no `f64`, and the nearest + // one is 2^53 — which still looks like an exact integer, so the rounding + // would have shipped as a plausible-looking off-by-one. + assert_eq!(scalar_value(&9_007_199_254_740_993_i64), "9007199254740993"); + assert_eq!(scalar_value(&-9_007_199_254_740_993_i64), "-9007199254740993"); + assert_eq!(scalar_value(&9_007_199_254_740_993_u64), "9007199254740993"); + // 2^53 itself is representable, and the boundary must not shift. + assert_eq!(scalar_value(&9_007_199_254_740_992_i64), "9007199254740992"); + // The extremes of each width, including the `u64` range above `i64::MAX` + // that only `as_u64` can read. + assert_eq!(scalar_value(&i64::MAX), "9223372036854775807"); + assert_eq!(scalar_value(&i64::MIN), "-9223372036854775808"); + assert_eq!(scalar_value(&u64::MAX), "18446744073709551615"); + assert_eq!(scalar_value(&9_223_372_036_854_775_808_u64), "9223372036854775808"); + // Ordinary small integers are unchanged by the integer-first branch. + assert_eq!(scalar_value(&100_i64), "100"); + assert_eq!(scalar_value(&-7_i32), "-7"); + assert_eq!(scalar_value(&42_u32), "42"); + } + #[test] fn deep_object_pairs_degrades_for_non_object_values() { // A loosely-typed param may hold anything at runtime; a scalar keeps the @@ -1396,21 +1468,22 @@ mod tests { #[cfg(feature = "tracing")] mod tracing_events { use super::*; - use std::sync::{Arc, Mutex}; + use std::cell::RefCell; + use std::sync::OnceLock; + + thread_local! { + /// Events seen on this thread while it is capturing, and `None` on + /// every thread that is not inside [`capture`] — which is what keeps + /// the rest of the suite's events out of an assertion here. + static CAPTURED: RefCell>> = const { RefCell::new(None) }; + } /// A minimal `tracing::Subscriber` that renders every event it sees as - /// `target message field=value …` text. + /// `target message field=value …` text into the capturing thread's buffer. /// /// Hand-rolled rather than pulled from `tracing-subscriber`: proving /// redaction must not add a dev-dependency to every generated crate. - #[derive(Clone, Default)] - struct Capture(Arc>>); - - impl Capture { - fn rendered(&self) -> String { - self.0.lock().expect("capture lock").join("\n") - } - } + struct Capture; /// Appends `name=value` for every field of an event, using the `Debug` /// fallback so string, integer, and `%`/`?` fields all render. @@ -1437,9 +1510,16 @@ mod tests { fn record_follows_from(&self, _span: &tracing::span::Id, _follows: &tracing::span::Id) {} fn event(&self, event: &tracing::Event<'_>) { - let mut rendered = event.metadata().target().to_owned(); - event.record(&mut Render(&mut rendered)); - self.0.lock().expect("capture lock").push(rendered); + CAPTURED.with(|captured| { + // Events reaching a thread that is not capturing are dropped: + // this subscriber is global, so it also sees the events every + // other test in this binary emits. + if let Some(events) = captured.borrow_mut().as_mut() { + let mut rendered = event.metadata().target().to_owned(); + event.record(&mut Render(&mut rendered)); + events.push(rendered); + } + }); } fn enter(&self, _span: &tracing::span::Id) {} @@ -1447,12 +1527,37 @@ mod tests { fn exit(&self, _span: &tracing::span::Id) {} } + /// Runs `emit` with this thread capturing, and renders what it emitted. + /// + /// The subscriber is installed globally, once, rather than per-test with + /// `tracing::subscriber::with_default`. `tracing` resolves each callsite's + /// `Interest` the first time that callsite is reached and caches it for the + /// whole process, while `with_default` only registers a subscriber on the + /// calling thread: a sibling test racing through the same `trace::*` + /// callsite can therefore pin it to `Interest::never()`, and the event this + /// test asserts on is then dropped before any subscriber sees it. A global + /// subscriber that is unconditionally `enabled` makes every callsite resolve + /// to `Interest::always()` whichever thread reaches it first; the + /// thread-local buffer above, not the subscriber, is what scopes a capture + /// to one test. + fn capture(emit: impl FnOnce()) -> String { + static INSTALLED: OnceLock<()> = OnceLock::new(); + INSTALLED.get_or_init(|| { + tracing::subscriber::set_global_default(Capture) + .expect("this test binary installs no other global subscriber"); + }); + CAPTURED.with(|captured| *captured.borrow_mut() = Some(Vec::new())); + emit(); + CAPTURED + .with(|captured| captured.borrow_mut().take()) + .expect("the capture buffer was armed just above") + .join("\n") + } + /// Every event the runtime emits for a retried request and a failed - /// decode, captured on one thread-local subscriber. + /// decode, captured on one thread. fn capture_request_and_decode_events() -> String { - let capture = Capture::default(); - let sink = capture.clone(); - tracing::subscriber::with_default(capture, || { + capture(|| { let runtime = tokio::runtime::Builder::new_current_thread() .build() .expect("a current-thread runtime must build"); @@ -1481,7 +1586,6 @@ mod tests { .await .expect_err("an object is not a u64"); }); - sink.rendered() }) } @@ -1530,10 +1634,7 @@ mod tests { /// alternative, and no part of the failed exchange's error. #[test] fn the_oauth_fallback_event_carries_no_fields() { - let capture = Capture::default(); - let sink = capture.clone(); - tracing::subscriber::with_default(capture, trace_oauth_fallback); - let rendered = sink.rendered(); + let rendered = capture(trace_oauth_fallback); assert!( rendered.contains("oauth token exchange failed"), "fallback event missing: {rendered}" diff --git a/src/lib.rs b/src/lib.rs index a494425..ec8c34b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,14 @@ #![recursion_limit = "512"] pub mod client; +#[doc(hidden)] +pub mod datetime; pub mod error; #[doc(hidden)] pub mod http; pub mod models; +#[doc(hidden)] +pub mod number; pub mod resources; #[doc(hidden)] pub mod streaming; @@ -27,12 +31,12 @@ pub use models::{ AccuracyPaginationDirection, AccuracyPaginationMetric, AccuracyPaginationMode, AccuracyScoreBreakdown, AccuracyThemeTrendPoint, AccuracyThemeTrendSeries, AccuracyTopicIdsQuery, AccuracyTrendPoint, AccuracyTrendSeriesMeta, AddFolderRequest, AdditionalTab, AeoScore, AeoScoreTargetZone, Agent, AgentDetail, - AgentGraph, AgentRun, AgentRunStatus, AgentRunStep, AgentSchema, AgentStatus, AgentValidation, - AgentValidationIssue, AgentVersion, AnalysisBreakdown, AnalysisInputs, AnalysisSummary, AnalysisType, - AnalysisTypeFilter, AnalysisTypeFilterOperator, AnalysisTypeFilterValue, AnalysisTypeFilterValueVariant1, - AnalysisTypeFilterValueVariant2, AnswerRow, AnswersQuery, AnswersQueryFilter, AnswersRawData, AnswersResponse, - AnswersResponseInfo, AnswersV2Info, AnswersV2Query, AnswersV2QueryInclude, AnswersV2Response, - AppRoutesV2AnswerEngineInsightsReportsQueryFanoutsSortSpec, + AgentGraph, AgentRun, AgentRunOutputDetail, AgentRunStatus, AgentRunStep, AgentSchema, AgentStatus, + AgentValidation, AgentValidationIssue, AgentVersion, AnalysisBreakdown, AnalysisInputs, AnalysisSummary, + AnalysisType, AnalysisTypeFilter, AnalysisTypeFilterOperator, AnalysisTypeFilterValue, + AnalysisTypeFilterValueVariant1, AnalysisTypeFilterValueVariant2, AnswerRow, AnswersQuery, AnswersQueryFilter, + AnswersRawData, AnswersResponse, AnswersResponseInfo, AnswersV2Info, AnswersV2Query, AnswersV2QueryInclude, + AnswersV2Response, AppRoutesV2AnswerEngineInsightsReportsQueryFanoutsSortSpec, AppRoutesV2AnswerEngineInsightsReportsQueryFanoutsSortSpecDir, AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpec, AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpecDir, @@ -82,10 +86,12 @@ pub use models::{ MerchantNameFilterValue, ModelIdFilter, ModelIdFilterField, ModelIdFilterOperator, ModelIdFilterValue, NamedResource, NamedResourceDiffList, NodeSchemaResponse, NodeTypeSummary, NumericMetricFilter, NumericMetricFilterOperator, NumericMetricFilterValue, OptimizationQuery, OrderByDirection, Organization, - OrganizationAssetsResponse, OrganizationPersonasWithCategory, OrganizationPersonasWithCategoryResponse, Pagination, - PaginationInfo, PathFilter, PathFilterOperator, PathFilterValue, PersonaIdFilter, PersonaIdFilterOperator, - PersonaIdFilterValue, PersonaProfile, PersonaProfileBehavior, PersonaProfileDemographics, PersonaProfileEmployment, - ProductNameFilter, ProductNameFilterOperator, ProductNameFilterValue, + OrganizationAssetsResponse, OrganizationPersonasWithCategory, OrganizationPersonasWithCategoryResponse, + OtfIntentShareData, OtfIntentSharesQuery, OtfIntentSharesQueryMatchingType, OtfIntentSharesQueryPlatform, + OtfIntentSharesResponse, OtfVolumeData, OtfVolumeRequest, OtfVolumeRequestMatchingType, OtfVolumeRequestPlatform, + OtfVolumeResponse, Pagination, PaginationInfo, PathFilter, PathFilterOperator, PathFilterValue, PersonaIdFilter, + PersonaIdFilterOperator, PersonaIdFilterValue, PersonaProfile, PersonaProfileBehavior, PersonaProfileDemographics, + PersonaProfileEmployment, ProductNameFilter, ProductNameFilterOperator, ProductNameFilterValue, ProfoundAnswerEngineInsightsFiltersAssetNameFilter, ProfoundAnswerEngineInsightsFiltersAssetNameFilterOperator, ProfoundAnswerEngineInsightsFiltersAssetNameFilterValue, Project, ProjectAttachment, ProjectDetail, ProjectDetailResponse, ProjectDetailSourceKind, ProjectDetailStatus, ProjectGeneration, ProjectGenerationContext, @@ -126,23 +132,23 @@ pub use models::{ SentimentV2ModelIdFilterOperator, SentimentV2ModelIdFilterValue, SentimentV2PersonaIdFilter, SentimentV2PersonaIdFilterOperator, SentimentV2PersonaIdFilterValue, SentimentV2PromptIdFilter, SentimentV2PromptIdFilterOperator, SentimentV2PromptIdFilterValue, SentimentV2Query, SentimentV2QueryGroupBy, - SentimentV2QueryInterval, SentimentV2QueryMetric, SentimentV2RegionIdFilter, SentimentV2RegionIdFilterOperator, - SentimentV2RegionIdFilterValue, SentimentV2ReportDataPoint, SentimentV2ReportGroupMetadata, - SentimentV2ReportGroupMetadataSentiment, SentimentV2ReportInfo, SentimentV2ReportQuery, - SentimentV2ReportQueryDateBucket, SentimentV2ReportQueryDimension, SentimentV2ReportQueryFilter, - SentimentV2ReportQueryMetric, SentimentV2ReportQueryOrderBy, SentimentV2ReportResponse, SentimentV2Response, - SentimentV2RunIdFilter, SentimentV2RunIdFilterOperator, SentimentV2RunIdFilterValue, SentimentV2SentimentFilter, - SentimentV2SentimentFilterOperator, SentimentV2SentimentFilterValue, SentimentV2SentimentFilterValueVariant1, - SentimentV2SentimentFilterValueVariant2, SentimentV2TagIdFilter, SentimentV2TagIdFilterOperator, - SentimentV2TagIdFilterValue, SentimentV2ThemeFilter, SentimentV2ThemeFilterOperator, SentimentV2ThemeFilterValue, - SentimentV2ThemeIdFilter, SentimentV2ThemeIdFilterOperator, SentimentV2ThemeIdFilterValue, - SentimentV2TopicIdFilter, SentimentV2TopicIdFilterOperator, SentimentV2TopicIdFilterValue, ShoppingBrandRow, - ShoppingBrandsV2Info, ShoppingBrandsV2InfoAssets, ShoppingBrandsV2Query, ShoppingBrandsV2QueryAssets, - ShoppingBrandsV2QueryGroupBy, ShoppingBrandsV2QueryInterval, ShoppingBrandsV2QueryMetric, - ShoppingBrandsV2QueryScope, ShoppingBrandsV2Response, ShoppingMerchantRow, ShoppingMerchantsV2Info, - ShoppingMerchantsV2Query, ShoppingMerchantsV2QueryGroupBy, ShoppingMerchantsV2QueryInterval, - ShoppingMerchantsV2QueryMetric, ShoppingMerchantsV2Response, ShoppingProductRow, ShoppingProductsV2Info, - ShoppingProductsV2Query, ShoppingProductsV2QueryGroupBy, ShoppingProductsV2QueryInterval, + SentimentV2QueryInterval, SentimentV2QueryMetric, SentimentV2QuerySource, SentimentV2RegionIdFilter, + SentimentV2RegionIdFilterOperator, SentimentV2RegionIdFilterValue, SentimentV2ReportDataPoint, + SentimentV2ReportGroupMetadata, SentimentV2ReportGroupMetadataSentiment, SentimentV2ReportInfo, + SentimentV2ReportQuery, SentimentV2ReportQueryDateBucket, SentimentV2ReportQueryDimension, + SentimentV2ReportQueryFilter, SentimentV2ReportQueryMetric, SentimentV2ReportQueryOrderBy, + SentimentV2ReportResponse, SentimentV2Response, SentimentV2RunIdFilter, SentimentV2RunIdFilterOperator, + SentimentV2RunIdFilterValue, SentimentV2SentimentFilter, SentimentV2SentimentFilterOperator, + SentimentV2SentimentFilterValue, SentimentV2SentimentFilterValueVariant1, SentimentV2SentimentFilterValueVariant2, + SentimentV2TagIdFilter, SentimentV2TagIdFilterOperator, SentimentV2TagIdFilterValue, SentimentV2ThemeFilter, + SentimentV2ThemeFilterOperator, SentimentV2ThemeFilterValue, SentimentV2ThemeIdFilter, + SentimentV2ThemeIdFilterOperator, SentimentV2ThemeIdFilterValue, SentimentV2TopicIdFilter, + SentimentV2TopicIdFilterOperator, SentimentV2TopicIdFilterValue, ShoppingBrandRow, ShoppingBrandsV2Info, + ShoppingBrandsV2InfoAssets, ShoppingBrandsV2Query, ShoppingBrandsV2QueryAssets, ShoppingBrandsV2QueryGroupBy, + ShoppingBrandsV2QueryInterval, ShoppingBrandsV2QueryMetric, ShoppingBrandsV2QueryScope, ShoppingBrandsV2Response, + ShoppingMerchantRow, ShoppingMerchantsV2Info, ShoppingMerchantsV2Query, ShoppingMerchantsV2QueryGroupBy, + ShoppingMerchantsV2QueryInterval, ShoppingMerchantsV2QueryMetric, ShoppingMerchantsV2Response, ShoppingProductRow, + ShoppingProductsV2Info, ShoppingProductsV2Query, ShoppingProductsV2QueryGroupBy, ShoppingProductsV2QueryInterval, ShoppingProductsV2QueryMetric, ShoppingProductsV2Response, ShoppingStreamBrandsResponse, ShoppingStreamMerchantsResponse, ShoppingStreamProductsResponse, ShoppingStreamTriggerRateResponse, ShoppingTriggerRateRow, ShoppingTriggerRateV2Info, ShoppingTriggerRateV2Query, ShoppingTriggerRateV2QueryGroupBy, @@ -166,7 +172,7 @@ pub use models::{ VisibilityQuery, VisibilityQueryDateInterval, VisibilityQueryDimension, VisibilityQueryFilter, VisibilityQueryMetric, VisibilityQueryOrderBy, VisibilityRow, VisibilityV2Info, VisibilityV2InfoAssetFilter, VisibilityV2Query, VisibilityV2QueryAssets, VisibilityV2QueryGroupBy, VisibilityV2QueryInterval, - VisibilityV2QueryMetric, VisibilityV2QueryScope, VisibilityV2Response, WebSearchResultsQuery, + VisibilityV2QueryMetric, VisibilityV2QueryScope, VisibilityV2Response, VolumeResponseInfo, WebSearchResultsQuery, WebSearchResultsQueryDateInterval, WebSearchResultsQueryDimension, WebSearchResultsQueryFilter, WebSearchResultsQueryMetric, WebSearchResultsQueryOrderBy, WebSearchResultsResponse, WebSearchResultsResult, WebSearchResultsResultDimension, WebSearchResultsResultMetric, WebSearchResultsStreamResponse, diff --git a/src/models/accuracy.rs b/src/models/accuracy.rs index fbf2bf8..57c194a 100644 --- a/src/models/accuracy.rs +++ b/src/models/accuracy.rs @@ -15,6 +15,8 @@ pub struct AccuracyBreakdownQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -52,6 +54,7 @@ pub struct AccuracyBreakdownQuery { pub sort_by: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub sort_order: Option, + /// Canonical grouped pagination plan for Accuracy Breakdown rows. #[serde(default, skip_serializing_if = "Option::is_none")] pub pagination: Option, } @@ -68,6 +71,7 @@ impl AccuracyBreakdownQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -115,12 +119,23 @@ pub struct AccuracyBreakdownRow { pub group_ids: Option>, #[serde(rename = "groupNames", default, skip_serializing_if = "Option::is_none")] pub group_names: Option>, + #[serde(serialize_with = "crate::number::serialize")] pub share: f64, - #[serde(rename = "shareChange", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "shareChange", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub share_change: Option, - #[serde(rename = "responseAccuracy")] + #[serde(rename = "responseAccuracy", serialize_with = "crate::number::serialize")] pub response_accuracy: f64, - #[serde(rename = "accuracyChange", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "accuracyChange", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub accuracy_change: Option, #[serde(rename = "accurateCount", default, skip_serializing_if = "Option::is_none")] pub accurate_count: Option, @@ -156,6 +171,13 @@ pub struct AccuracyCitationAnalysisResponse { pub page_title: String, #[serde(rename = "markdownContent")] pub markdown_content: String, + /// Whether the page preview was shortened. + #[serde( + rename = "markdownContentTruncated", + default, + skip_serializing_if = "Option::is_none" + )] + pub markdown_content_truncated: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub claims: Option>, } @@ -228,6 +250,8 @@ pub struct AccuracyOverviewQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -267,6 +291,7 @@ impl AccuracyOverviewQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -292,9 +317,14 @@ impl AccuracyOverviewQuery { pub struct AccuracyOverviewResponse { #[serde(rename = "trendByPeriod")] pub trend_by_period: Vec, - #[serde(rename = "overallAccuracy")] + #[serde(rename = "overallAccuracy", serialize_with = "crate::number::serialize")] pub overall_accuracy: f64, - #[serde(rename = "accuracyChange", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "accuracyChange", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub accuracy_change: Option, #[serde(rename = "scoreBreakdown")] pub score_breakdown: Vec, @@ -325,10 +355,16 @@ pub struct AccuracyPagination { pub struct AccuracyScoreBreakdown { pub status: String, pub count: i64, + #[serde(serialize_with = "crate::number::serialize")] pub share: f64, #[serde(rename = "countChange", default, skip_serializing_if = "Option::is_none")] pub count_change: Option, - #[serde(rename = "shareChange", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "shareChange", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub share_change: Option, } @@ -341,6 +377,7 @@ pub struct AccuracyThemeTrendPoint { pub date: String, pub total: i64, pub accurate: i64, + #[serde(serialize_with = "crate::number::serialize")] pub ratio: f64, } @@ -373,6 +410,7 @@ pub struct AccuracyTrendPoint { pub accurate: i64, #[serde(default, skip_serializing_if = "Option::is_none")] pub verified: Option, + #[serde(serialize_with = "crate::number::serialize")] pub ratio: f64, #[serde(rename = "prevPeriodData", default, skip_serializing_if = "Option::is_none")] pub prev_period_data: Option>, @@ -399,6 +437,8 @@ pub struct ClaimBreakdownQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -440,6 +480,7 @@ impl ClaimBreakdownQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -478,9 +519,14 @@ pub struct ClaimBreakdownRow { pub response_count: i64, #[serde(rename = "totalResponseCount")] pub total_response_count: i64, - #[serde(rename = "responseShare")] + #[serde(rename = "responseShare", serialize_with = "crate::number::serialize")] pub response_share: f64, - #[serde(rename = "responseShareDelta", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "responseShareDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub response_share_delta: Option, #[serde(rename = "prevResponseCount")] pub prev_response_count: i64, @@ -502,11 +548,18 @@ pub struct ClaimCitationRow { #[serde(rename = "domainCategory")] pub domain_category: String, pub snippet: String, - #[serde(rename = "citationCount")] + #[serde(rename = "citationCount", serialize_with = "crate::number::serialize")] pub citation_count: f64, - #[serde(rename = "citationShare")] + /// Citation share as a 0-100 percentage; 0.4718 means 0.4718%. + #[serde(rename = "citationShare", serialize_with = "crate::number::serialize")] pub citation_share: f64, - #[serde(rename = "citationShareDelta", default, skip_serializing_if = "Option::is_none")] + /// Change in citation share in percentage points. + #[serde( + rename = "citationShareDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub citation_share_delta: Option, } @@ -520,6 +573,8 @@ pub struct ClaimCitationsQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -569,6 +624,7 @@ impl ClaimCitationsQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -612,9 +668,14 @@ pub struct ClaimPromptBreakdownRow { pub response_count: i64, #[serde(rename = "totalResponseCount")] pub total_response_count: i64, - #[serde(rename = "responseShare")] + #[serde(rename = "responseShare", serialize_with = "crate::number::serialize")] pub response_share: f64, - #[serde(rename = "responseShareDelta", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "responseShareDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub response_share_delta: Option, #[serde(rename = "prevResponseCount")] pub prev_response_count: i64, @@ -659,6 +720,8 @@ pub struct ClusterExampleRunsQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -704,6 +767,7 @@ impl ClusterExampleRunsQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -741,6 +805,7 @@ pub struct ClusterExampleRunsResponse { pub struct ClusterModelShare { #[serde(rename = "modelId")] pub model_id: String, + #[serde(serialize_with = "crate::number::serialize")] pub occurrence: f64, } @@ -818,9 +883,16 @@ pub struct InaccuracyDriverRow { pub snippet: String, #[serde(rename = "snippetClaimId")] pub snippet_claim_id: String, - #[serde(rename = "claimOccurrence")] + /// Claim response share as a 0-100 percent, not citation share. + #[serde(rename = "claimOccurrence", serialize_with = "crate::number::serialize")] pub claim_occurrence: f64, - #[serde(rename = "claimOccurrenceDelta", default, skip_serializing_if = "Option::is_none")] + /// Change in claim occurrence in percentage points. + #[serde( + rename = "claimOccurrenceDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub claim_occurrence_delta: Option, pub href: String, #[serde(rename = "citationCategory")] @@ -841,6 +913,8 @@ pub struct InaccuracyDriversQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -878,6 +952,7 @@ impl InaccuracyDriversQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -928,9 +1003,14 @@ pub struct InaccurateClusterRow { pub response_count: i64, #[serde(rename = "totalResponseCount")] pub total_response_count: i64, - #[serde(rename = "responseShare")] + #[serde(rename = "responseShare", serialize_with = "crate::number::serialize")] pub response_share: f64, - #[serde(rename = "responseShareDelta", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "responseShareDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub response_share_delta: Option, #[serde(rename = "citationHostnames")] pub citation_hostnames: Vec, @@ -948,6 +1028,8 @@ pub struct InaccurateClustersQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -993,6 +1075,7 @@ impl InaccurateClustersQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1048,9 +1131,14 @@ pub struct InaccurateThemeRow { pub response_count: i64, #[serde(rename = "totalResponseCount")] pub total_response_count: i64, - #[serde(rename = "responseShare")] + #[serde(rename = "responseShare", serialize_with = "crate::number::serialize")] pub response_share: f64, - #[serde(rename = "responseShareDelta", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "responseShareDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub response_share_delta: Option, } @@ -1064,6 +1152,8 @@ pub struct InaccurateThemesQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -1109,6 +1199,7 @@ impl InaccurateThemesQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -1150,9 +1241,14 @@ pub struct TopInaccurateClaimRow { pub cluster_id: String, #[serde(rename = "canonicalClaim")] pub canonical_claim: String, - #[serde(rename = "claimOccurrence")] + #[serde(rename = "claimOccurrence", serialize_with = "crate::number::serialize")] pub claim_occurrence: f64, - #[serde(rename = "claimOccurrenceDelta", default, skip_serializing_if = "Option::is_none")] + #[serde( + rename = "claimOccurrenceDelta", + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub claim_occurrence_delta: Option, } @@ -1166,6 +1262,8 @@ pub struct TopInaccurateClaimsQuery { pub comparison_end_date: Option, pub category_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub languages: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] pub topic_ids: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub exclude_topic_ids: Option, @@ -1203,6 +1301,7 @@ impl TopInaccurateClaimsQuery { comparison_start_date: None, comparison_end_date: None, category_id: category_id.into(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, diff --git a/src/models/ad_account.rs b/src/models/ad_account.rs index aca4d85..8e2592f 100644 --- a/src/models/ad_account.rs +++ b/src/models/ad_account.rs @@ -55,16 +55,32 @@ pub struct InsightRow { #[serde(default, skip_serializing_if = "Option::is_none")] pub clicks: Option, /// Spend in the ad account's currency units. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub spend: Option, /// Click-through rate. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub ctr: Option, /// Cost per click. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub cpc: Option, /// Cost per thousand impressions. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub cpm: Option, } diff --git a/src/models/agents.rs b/src/models/agents.rs index 3ce7846..ef190f0 100644 --- a/src/models/agents.rs +++ b/src/models/agents.rs @@ -21,6 +21,7 @@ pub struct Agent { /// Current status of the agent. pub status: AgentStatus, /// When the agent was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// Short description of the agent, if provided. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -43,6 +44,7 @@ pub struct AgentDetail { /// Current status of the agent. pub status: AgentStatus, /// When the agent was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// Short description of the agent, if provided. #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/src/models/categories.rs b/src/models/categories.rs index 39c7dcd..4902739 100644 --- a/src/models/categories.rs +++ b/src/models/categories.rs @@ -68,6 +68,7 @@ pub struct CategoryAsset { #[serde(default, skip_serializing_if = "Option::is_none")] pub alternate_domains: Option>, pub is_owned: bool, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, pub logo_url: String, } @@ -359,7 +360,9 @@ pub struct Prompt { pub platforms: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub personas: Option>, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub updated_at: chrono::DateTime, } diff --git a/src/models/claims.rs b/src/models/claims.rs index 98ed129..5838299 100644 --- a/src/models/claims.rs +++ b/src/models/claims.rs @@ -21,7 +21,11 @@ pub struct ClaimCitationSource { pub domain_category: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub snippet: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub citation_share: Option, } @@ -52,7 +56,11 @@ pub struct ClaimModelOccurrence { #[serde(default, skip_serializing_if = "Option::is_none")] pub name: Option, /// Only populated for entries in `models`; omitted from grouped-section `model`. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, } @@ -67,7 +75,11 @@ pub struct FactcheckClaim { pub cluster_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub claim: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub theme: Option, @@ -79,6 +91,9 @@ pub struct FactcheckClaim { pub evidence: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub citation_sources: Option>, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -109,6 +124,9 @@ pub struct FactcheckClaimsInfo { /// Per-claim detail fields requested. #[serde(default, skip_serializing_if = "Option::is_none")] pub include: Option>, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// The inaccurate claims — flat + paginated, or `group_by` one dim to section them (all claims per section). @@ -181,7 +199,11 @@ pub struct FactcheckClaimsRow { pub cluster_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub claim: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub reasoning: Option, @@ -193,20 +215,29 @@ pub struct FactcheckClaimsRow { pub citation_sources: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub tag: Option, /// Claim theme (string), or an `{id, name}` ref when sectioning by `theme`. #[serde(default, skip_serializing_if = "Option::is_none")] pub theme: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub accuracy: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub accurate: Option, @@ -216,6 +247,9 @@ pub struct FactcheckClaimsRow { pub total_claims: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub claims: Option>, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/src/models/documents.rs b/src/models/documents.rs index 18eb6c1..3f91065 100644 --- a/src/models/documents.rs +++ b/src/models/documents.rs @@ -66,8 +66,10 @@ pub struct CreateDocumentResponse { /// General access scope. New documents are `invited_only`. pub visibility: CreateDocumentResponseVisibility, /// When the document was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// When the document was last modified. Seeding the initial content counts, so a freshly created document is normally modified a moment after it was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub updated_at: chrono::DateTime, /// ID of the document's owner. pub owner_user_id: String, @@ -106,8 +108,10 @@ pub struct Document { /// General access scope. New documents are `invited_only`. pub visibility: DocumentVisibility, /// When the document was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// When the document was last modified. Seeding the initial content counts, so a freshly created document is normally modified a moment after it was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub updated_at: chrono::DateTime, /// ID of the document's owner. pub owner_user_id: String, @@ -157,8 +161,10 @@ pub struct ReadDocumentResponse { /// General access scope. New documents are `invited_only`. pub visibility: ReadDocumentResponseVisibility, /// When the document was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// When the document was last modified. Seeding the initial content counts, so a freshly created document is normally modified a moment after it was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub updated_at: chrono::DateTime, /// ID of the document's owner. pub owner_user_id: String, @@ -234,8 +240,10 @@ pub struct ReplaceDocumentContentResponse { /// General access scope. New documents are `invited_only`. pub visibility: ReplaceDocumentContentResponseVisibility, /// When the document was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, /// When the document was last modified. Seeding the initial content counts, so a freshly created document is normally modified a moment after it was created. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub updated_at: chrono::DateTime, /// ID of the document's owner. pub owner_user_id: String, diff --git a/src/models/factcheck.rs b/src/models/factcheck.rs index cb9708c..92e8bce 100644 --- a/src/models/factcheck.rs +++ b/src/models/factcheck.rs @@ -26,28 +26,42 @@ pub struct CitationScoreRef { pub struct FactcheckScoreRow { #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub tag: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub theme: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub citation: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub accuracy: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub accurate: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub inaccurate: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -75,6 +89,9 @@ pub struct FactcheckScoresInfo { pub filter: Option, /// Dimensions the scores are sliced by (empty → headline). pub group_by: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Accuracy scores. `group_by` picks the slice (one or two dimensions); empty → the headline score. diff --git a/src/models/generations.rs b/src/models/generations.rs index fb39b14..9b676c1 100644 --- a/src/models/generations.rs +++ b/src/models/generations.rs @@ -29,9 +29,17 @@ pub struct ProjectGenerationStatus { #[serde(default, skip_serializing_if = "Option::is_none")] pub focus_prompt: Option, pub status: ProjectGenerationStatusStatus, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub started_at: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub finished_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, diff --git a/src/models/intents.rs b/src/models/intents.rs new file mode 100644 index 0000000..b3d0a1f --- /dev/null +++ b/src/models/intents.rs @@ -0,0 +1,160 @@ +// Code generated by Scalar SDK Generator. DO NOT EDIT. +//! Generated request and response models declared under `intents`. + +#[allow(unused_imports)] +use super::*; +use serde::{Deserialize, Serialize}; + +/// Intent share averaged across the requested cohort date range. +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct OtfIntentShareData { + pub main_classification: String, + #[serde(default)] + pub sub_category_classification: Option, + #[serde(serialize_with = "crate::number::serialize")] + pub pct_conversation: f64, +} + +/// Query for on-the-fly intent shares for a single keyword. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OtfIntentSharesQuery { + pub keyword: String, + pub matching_type: OtfIntentSharesQueryMatchingType, + pub start_date: chrono::NaiveDate, + pub end_date: chrono::NaiveDate, + /// ISO 3166-1 alpha-3 country codes (e.g. USA). Omit to include every region. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub regions: Option>, + /// Platforms to restrict to. Omit to include every platform. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub platforms: Option>, +} + +impl OtfIntentSharesQuery { + /// Creates a new `OtfIntentSharesQuery` from its required fields, leaving every optional one unset. + /// + /// Pair it with struct-update syntax to set only the optionals you need: + /// `OtfIntentSharesQuery { regions: Some(…), ..OtfIntentSharesQuery::new(…) }`. + pub fn new( + keyword: impl Into, + matching_type: OtfIntentSharesQueryMatchingType, + start_date: chrono::NaiveDate, + end_date: chrono::NaiveDate, + ) -> Self { + Self { + keyword: keyword.into(), + matching_type, + start_date, + end_date, + regions: None, + platforms: None, + } + } +} + +/// Response wrapper for on-the-fly intent shares. +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct OtfIntentSharesResponse { + /// Metadata for volume query response. + /// truncated indicates if results hit MAX_ROWS limit. + pub info: VolumeResponseInfo, + pub data: Vec, +} + +/// Match on `as_str()` (or build one with `From<&str>`) when you need a value +/// this SDK version does not know. Matching `Unknown(_)` directly is an +/// anti-pattern: that arm silently stops matching once a future SDK release +/// adds the real variant. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OtfIntentSharesQueryMatchingType { + #[serde(rename = "exact_match")] + ExactMatch, + #[serde(rename = "phrase_match")] + PhraseMatch, + /// A value not known to this version of the SDK, preserved verbatim. + #[serde(untagged)] + Unknown(String), +} + +impl OtfIntentSharesQueryMatchingType { + /// The wire value this variant serializes to. + pub fn as_str(&self) -> &str { + match self { + Self::ExactMatch => "exact_match", + Self::PhraseMatch => "phrase_match", + Self::Unknown(value) => value, + } + } +} + +impl std::fmt::Display for OtfIntentSharesQueryMatchingType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl From<&str> for OtfIntentSharesQueryMatchingType { + fn from(value: &str) -> Self { + match value { + "exact_match" => Self::ExactMatch, + "phrase_match" => Self::PhraseMatch, + other => Self::Unknown(other.to_owned()), + } + } +} + +/// Match on `as_str()` (or build one with `From<&str>`) when you need a value +/// this SDK version does not know. Matching `Unknown(_)` directly is an +/// anti-pattern: that arm silently stops matching once a future SDK release +/// adds the real variant. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OtfIntentSharesQueryPlatform { + #[serde(rename = "chatgpt.com")] + ChatgptCom, + #[serde(rename = "gemini.google.com")] + GeminiGoogleCom, + #[serde(rename = "perplexity.ai")] + PerplexityAi, + /// A value not known to this version of the SDK, preserved verbatim. + #[serde(untagged)] + Unknown(String), +} + +impl OtfIntentSharesQueryPlatform { + /// The wire value this variant serializes to. + pub fn as_str(&self) -> &str { + match self { + Self::ChatgptCom => "chatgpt.com", + Self::GeminiGoogleCom => "gemini.google.com", + Self::PerplexityAi => "perplexity.ai", + Self::Unknown(value) => value, + } + } +} + +impl std::fmt::Display for OtfIntentSharesQueryPlatform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl From<&str> for OtfIntentSharesQueryPlatform { + fn from(value: &str) -> Self { + match value { + "chatgpt.com" => Self::ChatgptCom, + "gemini.google.com" => Self::GeminiGoogleCom, + "perplexity.ai" => Self::PerplexityAi, + other => Self::Unknown(other.to_owned()), + } + } +} diff --git a/src/models/knowledge_bases.rs b/src/models/knowledge_bases.rs index ea0926a..1bb2895 100644 --- a/src/models/knowledge_bases.rs +++ b/src/models/knowledge_bases.rs @@ -22,6 +22,7 @@ pub struct KnowledgeBaseItem { #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, /// Creation timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, } @@ -47,6 +48,7 @@ pub struct KnowledgeBaseSearchResult { /// Document or chunk path. pub id: String, /// Relevance score. + #[serde(serialize_with = "crate::number::serialize")] pub score: f64, /// Result metadata, including folder path. pub metadata: serde_json::Value, diff --git a/src/models/mod.rs b/src/models/mod.rs index 0054567..7bdd2c6 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -12,6 +12,7 @@ pub mod factcheck; pub mod folders; pub mod generations; pub mod integrations; +pub mod intents; pub mod knowledge_bases; pub mod knowledge_bases_documents; pub mod node_types; @@ -24,6 +25,7 @@ pub mod runs; pub mod shared; pub mod shopping; pub mod tasks; +pub mod volume; pub mod web_search_results; pub mod youtube; @@ -38,6 +40,7 @@ pub use factcheck::*; pub use folders::*; pub use generations::*; pub use integrations::*; +pub use intents::*; pub use knowledge_bases::*; pub use knowledge_bases_documents::*; pub use node_types::*; @@ -50,5 +53,6 @@ pub use runs::*; pub use shared::*; pub use shopping::*; pub use tasks::*; +pub use volume::*; pub use web_search_results::*; pub use youtube::*; diff --git a/src/models/optimization.rs b/src/models/optimization.rs index 45f2d33..ffe84ab 100644 --- a/src/models/optimization.rs +++ b/src/models/optimization.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; #[non_exhaustive] pub struct AeoScore { pub target_zone: AeoScoreTargetZone, + #[serde(serialize_with = "crate::number::serialize")] pub value: f64, } @@ -21,7 +22,9 @@ pub struct AeoScore { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct AeoScoreTargetZone { + #[serde(serialize_with = "crate::number::serialize")] pub low: f64, + #[serde(serialize_with = "crate::number::serialize")] pub high: f64, } @@ -32,7 +35,9 @@ pub struct AeoScoreTargetZone { #[non_exhaustive] pub struct AnalysisBreakdown { pub title: String, + #[serde(serialize_with = "crate::number::serialize")] pub weight: f64, + #[serde(serialize_with = "crate::number::serialize")] pub score: f64, } @@ -124,6 +129,7 @@ impl From<&str> for ContentFormat { pub struct ContentOptimization { pub id: String, pub title: String, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, #[serde(default)] pub extracted_input: Option, @@ -259,6 +265,7 @@ pub struct Recommendation { #[non_exhaustive] pub struct RecommendationImpact { pub section: String, + #[serde(serialize_with = "crate::number::serialize")] pub score: f64, } diff --git a/src/models/organizations.rs b/src/models/organizations.rs index b4d4061..138dea2 100644 --- a/src/models/organizations.rs +++ b/src/models/organizations.rs @@ -29,6 +29,7 @@ pub struct CategoryAssetWithCategory { #[serde(default, skip_serializing_if = "Option::is_none")] pub alternate_domains: Option>, pub is_owned: bool, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, pub logo_url: String, pub category: Category, @@ -42,6 +43,7 @@ pub struct CategoryAssetWithCategory { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct DomainWithOrganization { + #[serde(deserialize_with = "crate::datetime::deserialize")] pub created_at: chrono::DateTime, pub id: String, pub name: String, diff --git a/src/models/projects.rs b/src/models/projects.rs index c0a86ba..5f52e62 100644 --- a/src/models/projects.rs +++ b/src/models/projects.rs @@ -66,9 +66,17 @@ pub struct ListProjectsResponse { pub struct LiveGeneration { pub run_id: String, pub status: LiveGenerationStatus, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub started_at: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub finished_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, @@ -107,7 +115,11 @@ pub struct Project { pub task_count: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub new_task_count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub retired_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub retired_reason: Option, @@ -115,9 +127,17 @@ pub struct Project { pub latest_version_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub version_count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub created_at: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub updated_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub live_generation: Option, @@ -165,7 +185,11 @@ pub struct ProjectDetail { pub task_count: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub new_task_count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub retired_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub retired_reason: Option, @@ -173,9 +197,17 @@ pub struct ProjectDetail { pub latest_version_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub version_count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub created_at: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub updated_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub live_generation: Option, @@ -294,7 +326,11 @@ pub struct ProjectListItem { pub initiated_by_user_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub topics: Option>, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub updated_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub task_types: Option>, @@ -302,7 +338,11 @@ pub struct ProjectListItem { pub task_count: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub new_task_count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub retired_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub retired_reason: Option, @@ -330,7 +370,11 @@ pub struct ProjectStatusPayload { pub status: ProjectStatusPayloadStatus, #[serde(default, skip_serializing_if = "Option::is_none")] pub live_generation: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub updated_at: Option>, } diff --git a/src/models/prompts.rs b/src/models/prompts.rs index 39df4c3..5c7fece 100644 --- a/src/models/prompts.rs +++ b/src/models/prompts.rs @@ -16,6 +16,7 @@ pub struct AnswerRow { pub run_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -47,13 +48,18 @@ pub struct AnswerRow { pub analysis_types: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub sentiment_claims: Option>, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Body for the answers endpoint. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AnswersQuery { pub category_id: String, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// Pagination parameters for the results. Default is 10,000 rows with no offset. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -95,7 +101,11 @@ impl AnswersQuery { pub struct AnswersRawData { #[serde(default, skip_serializing_if = "Option::is_none")] pub run_id: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub created_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, @@ -189,6 +199,9 @@ pub struct AnswersV2Info { pub filter: Option, /// Row fields returned (echoes `include`, or all fields when omitted). pub include: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -373,6 +386,7 @@ pub struct FilterNode { pub and: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub or: Option>, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub not: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/src/models/reports.rs b/src/models/reports.rs index af501db..3972c4c 100644 --- a/src/models/reports.rs +++ b/src/models/reports.rs @@ -53,9 +53,14 @@ pub struct BotsReportQuery { /// Domain to query logs for. pub domain: String, /// Start date for logs. Accepts: YYYY-MM-DD, YYYY-MM-DD HH:MM, YYYY-MM-DD HH:MM:SS, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for logs. Accepts same formats as start_date. Defaults to now if omitted. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub end_date: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub organization_id: Option, @@ -119,9 +124,14 @@ pub struct BotsReportQueryV2 { /// Domain to query logs for. pub domain: String, /// Start date for logs. Accepts: YYYY-MM-DD, YYYY-MM-DD HH:MM, YYYY-MM-DD HH:MM:SS, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date in UTC. Accepts same formats as start_date. Defaults to now UTC if omitted. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub end_date: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub organization_id: Option, @@ -198,23 +208,35 @@ pub struct CitationRow { pub rank: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub count: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub citation_share: Option, /// Pages only. #[serde(default, skip_serializing_if = "Option::is_none")] pub first_cited_at: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -245,8 +267,10 @@ pub struct CitationsQuery { pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the citations report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -328,6 +352,9 @@ pub struct CitationsV2Info { pub metrics: Vec, /// Analysis types the citations were drawn from. pub analysis_types: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -465,16 +492,31 @@ pub struct QueryFanoutRow { pub prompt: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub query: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub total_fanouts: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub fanouts_per_execution: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub share: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub query_variations: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub rank: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -495,8 +537,10 @@ pub struct QueryFanoutsQuery { pub pagination: Option, pub category_id: String, /// Start date. Accepts YYYY-MM-DD, YYYY-MM-DD HH:MM, or ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date. Accepts YYYY-MM-DD, YYYY-MM-DD HH:MM, or ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// Filters to apply to the query fanout report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -555,6 +599,9 @@ pub struct QueryFanoutsV2Info { pub metrics: Vec, /// Caveat about which runs the fanout metrics cover. pub coverage_note: String, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -570,6 +617,7 @@ pub struct QueryFanoutsV2Query { pub metrics: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub interval: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -661,9 +709,14 @@ pub struct ReferralsQuery { /// Domain to query logs for. pub domain: String, /// Start date for logs. Accepts: YYYY-MM-DD, YYYY-MM-DD HH:MM, YYYY-MM-DD HH:MM:SS, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for logs. Accepts same formats as start_date. Defaults to now if omitted. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub end_date: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub organization_id: Option, @@ -727,9 +780,14 @@ pub struct ReferralsQueryV2 { /// Domain to query logs for. pub domain: String, /// Start date for logs. Accepts: YYYY-MM-DD, YYYY-MM-DD HH:MM, YYYY-MM-DD HH:MM:SS, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date in UTC. Accepts same formats as start_date. Defaults to now UTC if omitted. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub end_date: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub organization_id: Option, @@ -813,12 +871,33 @@ pub struct RootDomainFilter { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct SentimentMetrics { - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub positive_sentiment: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub negative_sentiment: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] + pub citation_share: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -858,8 +937,10 @@ pub struct SentimentQuery { pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the sentiment report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -900,32 +981,62 @@ impl SentimentQuery { pub struct SentimentRow { #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub tag: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub theme: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub claim: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub run: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub competitor: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + pub page: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub positive_sentiment: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub negative_sentiment: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] + pub citation_share: Option, /// Comparison-window metrics (when requested). #[serde(default, skip_serializing_if = "Option::is_none")] pub previous: Option, @@ -935,6 +1046,9 @@ pub struct SentimentRow { pub cited_websites: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub rank: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -943,10 +1057,16 @@ pub struct SentimentRow { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[non_exhaustive] pub struct SentimentScores { + #[serde(serialize_with = "crate::number::serialize")] pub positive_sentiment: f64, + #[serde(serialize_with = "crate::number::serialize")] pub negative_sentiment: f64, pub assessment_count: i64, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub occurrence: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub response_count: Option, @@ -1003,6 +1123,9 @@ pub struct SentimentV2Info { /// Comparison-window end (when requested). #[serde(default, skip_serializing_if = "Option::is_none")] pub comparison_end_date: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -1045,11 +1168,14 @@ pub struct SentimentV2Query { #[serde(default, skip_serializing_if = "Option::is_none")] pub comparison_end_date: Option, #[serde(default, skip_serializing_if = "Option::is_none")] + pub source: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub group_by: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub metrics: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub interval: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1085,6 +1211,7 @@ impl SentimentV2Query { end_date: end_date.into(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -1184,14 +1311,24 @@ pub struct SentimentV2ReportQuery { pub category_id: String, pub asset_name: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// Start of the previous period for delta computation. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub comparison_start_date: Option>, /// End of the previous period for delta computation. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub comparison_end_date: Option>, /// Date bucket for the report. Only used when dimensions includes date. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1332,12 +1469,15 @@ pub struct StreamCitationsQuery { /// #[serde(default, skip_serializing_if = "Option::is_none")] pub order_by: Option>, + /// Offset-based pagination parameters. #[serde(default, skip_serializing_if = "Option::is_none")] pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the citations report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1389,12 +1529,15 @@ pub struct StreamSentimentQuery { /// #[serde(default, skip_serializing_if = "Option::is_none")] pub order_by: Option>, + /// Offset-based pagination parameters. #[serde(default, skip_serializing_if = "Option::is_none")] pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the sentiment report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1446,12 +1589,15 @@ pub struct StreamVisibilityQuery { /// #[serde(default, skip_serializing_if = "Option::is_none")] pub order_by: Option>, + /// Offset-based pagination parameters. #[serde(default, skip_serializing_if = "Option::is_none")] pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the visibility report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1544,8 +1690,10 @@ pub struct VisibilityQuery { pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the visibility report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -1591,22 +1739,42 @@ pub struct VisibilityRow { pub rank: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub model: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub visibility_score: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub share_of_voice: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub average_position: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -1637,6 +1805,9 @@ pub struct VisibilityV2Info { /// Echoed `assets` selection (filter clause, name, or list), or null. #[serde(default, skip_serializing_if = "Option::is_none")] pub asset_filter: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -1657,6 +1828,7 @@ pub struct VisibilityV2Query { /// A name (`is`), a list (`in`), or {op,value} with op `is`/`in`/`not_in`. #[serde(default, skip_serializing_if = "Option::is_none")] pub assets: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -2094,8 +2266,6 @@ pub enum BotProviderFilterValueVariant1 { You, #[serde(rename = "you.com")] YouCom, - #[serde(rename = "xai")] - Xai, #[serde(rename = "grok")] Grok, #[serde(rename = "gemini")] @@ -2114,6 +2284,8 @@ pub enum BotProviderFilterValueVariant1 { Commoncrawl, #[serde(rename = "openclaw")] Openclaw, + #[serde(rename = "exa")] + Exa, /// A value not known to this version of the SDK, preserved verbatim. #[serde(untagged)] Unknown(String), @@ -2137,7 +2309,6 @@ impl BotProviderFilterValueVariant1 { Self::Duckduckgo => "duckduckgo", Self::You => "you", Self::YouCom => "you.com", - Self::Xai => "xai", Self::Grok => "grok", Self::Gemini => "gemini", Self::Mistral => "mistral", @@ -2147,6 +2318,7 @@ impl BotProviderFilterValueVariant1 { Self::Yahoo => "yahoo", Self::Commoncrawl => "commoncrawl", Self::Openclaw => "openclaw", + Self::Exa => "exa", Self::Unknown(value) => value, } } @@ -2175,7 +2347,6 @@ impl From<&str> for BotProviderFilterValueVariant1 { "duckduckgo" => Self::Duckduckgo, "you" => Self::You, "you.com" => Self::YouCom, - "xai" => Self::Xai, "grok" => Self::Grok, "gemini" => Self::Gemini, "mistral" => Self::Mistral, @@ -2185,6 +2356,7 @@ impl From<&str> for BotProviderFilterValueVariant1 { "yahoo" => Self::Yahoo, "commoncrawl" => Self::Commoncrawl, "openclaw" => Self::Openclaw, + "exa" => Self::Exa, other => Self::Unknown(other.to_owned()), } } @@ -2225,8 +2397,6 @@ pub enum BotProviderFilterValueVariant2 { You, #[serde(rename = "you.com")] YouCom, - #[serde(rename = "xai")] - Xai, #[serde(rename = "grok")] Grok, #[serde(rename = "gemini")] @@ -2245,6 +2415,8 @@ pub enum BotProviderFilterValueVariant2 { Commoncrawl, #[serde(rename = "openclaw")] Openclaw, + #[serde(rename = "exa")] + Exa, /// A value not known to this version of the SDK, preserved verbatim. #[serde(untagged)] Unknown(String), @@ -2268,7 +2440,6 @@ impl BotProviderFilterValueVariant2 { Self::Duckduckgo => "duckduckgo", Self::You => "you", Self::YouCom => "you.com", - Self::Xai => "xai", Self::Grok => "grok", Self::Gemini => "gemini", Self::Mistral => "mistral", @@ -2278,6 +2449,7 @@ impl BotProviderFilterValueVariant2 { Self::Yahoo => "yahoo", Self::Commoncrawl => "commoncrawl", Self::Openclaw => "openclaw", + Self::Exa => "exa", Self::Unknown(value) => value, } } @@ -2306,7 +2478,6 @@ impl From<&str> for BotProviderFilterValueVariant2 { "duckduckgo" => Self::Duckduckgo, "you" => Self::You, "you.com" => Self::YouCom, - "xai" => Self::Xai, "grok" => Self::Grok, "gemini" => Self::Gemini, "mistral" => Self::Mistral, @@ -2316,6 +2487,7 @@ impl From<&str> for BotProviderFilterValueVariant2 { "yahoo" => Self::Yahoo, "commoncrawl" => Self::Commoncrawl, "openclaw" => Self::Openclaw, + "exa" => Self::Exa, other => Self::Unknown(other.to_owned()), } } @@ -5894,6 +6066,49 @@ pub enum SentimentV2PromptIdFilterValue { Unknown(serde_json::Value), } +/// Match on `as_str()` (or build one with `From<&str>`) when you need a value +/// this SDK version does not know. Matching `Unknown(_)` directly is an +/// anti-pattern: that arm silently stops matching once a future SDK release +/// adds the real variant. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub enum SentimentV2QuerySource { + #[serde(rename = "response")] + Response, + #[serde(rename = "citation")] + Citation, + /// A value not known to this version of the SDK, preserved verbatim. + #[serde(untagged)] + Unknown(String), +} + +impl SentimentV2QuerySource { + /// The wire value this variant serializes to. + pub fn as_str(&self) -> &str { + match self { + Self::Response => "response", + Self::Citation => "citation", + Self::Unknown(value) => value, + } + } +} + +impl std::fmt::Display for SentimentV2QuerySource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl From<&str> for SentimentV2QuerySource { + fn from(value: &str) -> Self { + match value { + "response" => Self::Response, + "citation" => Self::Citation, + other => Self::Unknown(other.to_owned()), + } + } +} + /// Match on `as_str()` (or build one with `From<&str>`) when you need a value /// this SDK version does not know. Matching `Unknown(_)` directly is an /// anti-pattern: that arm silently stops matching once a future SDK release @@ -5986,6 +6201,8 @@ pub enum SentimentV2QueryMetric { NegativeSentiment, #[serde(rename = "occurrence")] Occurrence, + #[serde(rename = "citation_share")] + CitationShare, /// A value not known to this version of the SDK, preserved verbatim. #[serde(untagged)] Unknown(String), @@ -5998,6 +6215,7 @@ impl SentimentV2QueryMetric { Self::PositiveSentiment => "positive_sentiment", Self::NegativeSentiment => "negative_sentiment", Self::Occurrence => "occurrence", + Self::CitationShare => "citation_share", Self::Unknown(value) => value, } } @@ -6015,6 +6233,7 @@ impl From<&str> for SentimentV2QueryMetric { "positive_sentiment" => Self::PositiveSentiment, "negative_sentiment" => Self::NegativeSentiment, "occurrence" => Self::Occurrence, + "citation_share" => Self::CitationShare, other => Self::Unknown(other.to_owned()), } } @@ -8711,6 +8930,8 @@ pub enum AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpecField { PositiveSentiment, #[serde(rename = "negative_sentiment")] NegativeSentiment, + #[serde(rename = "citation_share")] + CitationShare, /// A value not known to this version of the SDK, preserved verbatim. #[serde(untagged)] Unknown(String), @@ -8723,6 +8944,7 @@ impl AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpecField { Self::Occurrence => "occurrence", Self::PositiveSentiment => "positive_sentiment", Self::NegativeSentiment => "negative_sentiment", + Self::CitationShare => "citation_share", Self::Unknown(value) => value, } } @@ -8740,6 +8962,7 @@ impl From<&str> for AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpecField "occurrence" => Self::Occurrence, "positive_sentiment" => Self::PositiveSentiment, "negative_sentiment" => Self::NegativeSentiment, + "citation_share" => Self::CitationShare, other => Self::Unknown(other.to_owned()), } } diff --git a/src/models/runs.rs b/src/models/runs.rs index 4450ec1..29b50b5 100644 --- a/src/models/runs.rs +++ b/src/models/runs.rs @@ -19,7 +19,11 @@ pub struct AcceptedAgentRun { /// Initial status of the accepted run. pub status: AgentRunStatus, /// When the run started, if execution began immediately. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub started_at: Option>, } @@ -37,22 +41,46 @@ pub struct AgentRun { /// Current status of the run. pub status: AgentRunStatus, /// When the run started, if it has started. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub started_at: Option>, /// When the run finished, if it has completed. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub finished_at: Option>, /// Error details, when the run fails and error information is available. #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, - /// Output values returned by the run, keyed by variable ID. This object conforms to `schema.output` from the agent detail response and is empty when no outputs are available. + /// Output values returned by the run, keyed by output-variable UUID. This UUID-keyed object is retained for compatibility and is empty when no outputs are available. #[serde(default, skip_serializing_if = "Option::is_none")] pub outputs: Option, + /// Expanded form of `outputs`, keyed by the same output-variable UUIDs. Each entry carries the agent's configured human-readable key as `title` alongside the returned value. `title` is null when the agent has no configured key for that output. Entries preserve the key order of `outputs`. The UUID-keyed `outputs` field remains the stable compatibility field. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub outputs_expanded: Option>, /// Ordered step-by-step execution trace — one entry per node that ran, in execution order. Always present once the run has executed a node; per-node `outputs` inside each step are included only when the request asks for `verbose`. #[serde(default, skip_serializing_if = "Option::is_none")] pub steps: Option>, } +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct AgentRunOutputDetail { + /// The agent's configured, human-readable key for this output. null when the agent has no configured key for this output. + #[serde(default)] + pub title: Option, + /// Value returned for the output variable. + pub value: serde_json::Value, +} + /// Current execution status for an agent run. /// /// Match on `as_str()` (or build one with `From<&str>`) when you need a value @@ -137,10 +165,18 @@ pub struct AgentRunStep { /// Terminal status of this node execution. pub status: String, /// Wall-clock seconds the node took, if recorded. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub elapsed_time: Option, /// When the node finished, if it has. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub finished_at: Option>, /// Failure detail for this node, when it failed. #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/src/models/shopping.rs b/src/models/shopping.rs index d66021c..524e59d 100644 --- a/src/models/shopping.rs +++ b/src/models/shopping.rs @@ -55,19 +55,33 @@ pub struct ShoppingBrandRow { pub rank: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub visibility_score: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub average_position: Option, /// Asset visibility rank (present on grouped rows). #[serde(default, skip_serializing_if = "Option::is_none")] pub visibility_rank: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -100,6 +114,9 @@ pub struct ShoppingBrandsV2Info { pub assets: Option, /// Metrics returned per row. pub metrics: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -120,6 +137,7 @@ pub struct ShoppingBrandsV2Query { /// Restrict to these asset names (a name or list). Overrides `scope`. #[serde(default, skip_serializing_if = "Option::is_none")] pub assets: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, /// Page size for scope=all; default 10, max 50. @@ -175,11 +193,19 @@ pub struct ShoppingBrandsV2Response { pub struct ShoppingMerchantRow { #[serde(default, skip_serializing_if = "Option::is_none")] pub merchant_name: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub merchant_share: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub merchant_share_rank: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub merchant_visibility: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub merchant_visibility_rank: Option, @@ -189,14 +215,25 @@ pub struct ShoppingMerchantRow { pub visibility_rank: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub brand_name: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub brand_share: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub product: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub product_visibility: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub product_rank: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -226,6 +263,9 @@ pub struct ShoppingMerchantsV2Info { pub view: String, /// Metrics returned per row. pub metrics: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -243,6 +283,7 @@ pub struct ShoppingMerchantsV2Query { pub metrics: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub interval: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, /// Page size; default 10, max 50. @@ -297,25 +338,55 @@ pub struct ShoppingProductRow { pub product: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub visibility_score: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub average_position: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub visibility_rank: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub position1_percentage: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub position2_percentage: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub position3_percentage: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub position_above3_percentage: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub product_rating: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub product_num_reviews: Option, @@ -326,6 +397,9 @@ pub struct ShoppingProductRow { /// Per-product merchant offers `{name, price}` (only with `include_merchants`). #[serde(default, skip_serializing_if = "Option::is_none")] pub merchants: Option>, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -355,6 +429,9 @@ pub struct ShoppingProductsV2Info { pub include_merchants: bool, /// Metrics returned per row. pub metrics: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -379,6 +456,7 @@ pub struct ShoppingProductsV2Query { /// Competitors returned when `target_product` is set. #[serde(default, skip_serializing_if = "Option::is_none")] pub competitor_limit: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, /// Page size; default 10, max 50. @@ -434,20 +512,31 @@ pub struct ShoppingProductsV2Response { pub struct ShoppingTriggerRateRow { #[serde(default, skip_serializing_if = "Option::is_none")] pub date: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub topic: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub region: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub persona: Option, + /// An ``{id, name}`` reference for a grouped dimension value. #[serde(default, skip_serializing_if = "Option::is_none")] pub prompt: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub total_runs: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub shopping_triggered_runs: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "crate::number::option::serialize" + )] pub trigger_rate_percentage: Option, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// @@ -475,6 +564,9 @@ pub struct ShoppingTriggerRateV2Info { pub filter: Option, /// Metrics returned per row. pub metrics: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -491,6 +583,7 @@ pub struct ShoppingTriggerRateV2Query { pub metrics: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub interval: Option, + /// A leaf (`field`/`op`/`value`) or an `and`/`or`/`not` group. #[serde(default, skip_serializing_if = "Option::is_none")] pub filter: Option, /// Page size; default 10, max 50. diff --git a/src/models/tasks.rs b/src/models/tasks.rs index cc1531f..a2e9bec 100644 --- a/src/models/tasks.rs +++ b/src/models/tasks.rs @@ -84,11 +84,19 @@ pub struct ProjectTask { pub reference_label: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub status: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub status_changed_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub is_new: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub created_at: Option>, } @@ -118,11 +126,19 @@ pub struct ProjectTaskDetail { pub reference_label: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub status: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub status_changed_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub is_new: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "crate::datetime::option::deserialize" + )] pub created_at: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub project_title: Option, @@ -155,6 +171,7 @@ pub struct ProjectTaskStatusPayload { pub task_id: String, pub project_id: String, pub status: ProjectTaskStatusPayloadStatus, + #[serde(deserialize_with = "crate::datetime::deserialize")] pub changed_at: chrono::DateTime, #[serde(default, skip_serializing_if = "Option::is_none")] pub changed_by: Option, diff --git a/src/models/volume.rs b/src/models/volume.rs new file mode 100644 index 0000000..fe260b8 --- /dev/null +++ b/src/models/volume.rs @@ -0,0 +1,184 @@ +// Code generated by Scalar SDK Generator. DO NOT EDIT. +//! Generated request and response models declared under `volume`. + +#[allow(unused_imports)] +use super::*; +use serde::{Deserialize, Serialize}; + +/// Single projected volume row. +/// +/// Each keyword query returns both weekly and monthly rows in one response; +/// frequency distinguishes them and matches the fast table's values +/// ('Week - Monday Start' / 'Month'). +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct OtfVolumeData { + pub country_code: String, + pub platform: String, + pub matching_type: String, + pub frequency: String, + pub date: chrono::NaiveDate, + #[serde(serialize_with = "crate::number::serialize")] + pub volume: f64, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OtfVolumeRequest { + pub keyword: String, + pub matching_type: OtfVolumeRequestMatchingType, + pub start_date: chrono::NaiveDate, + pub end_date: chrono::NaiveDate, + /// ISO 3166-1 alpha-3 country codes (e.g. USA). Omit to include every region. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub regions: Option>, + /// Platforms to restrict to. Omit to include every platform. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub platforms: Option>, + /// Organization whose daily keyword allowance is used. Required in the JSON request body for API keys with multiple organizations that have API access. If omitted or null, defaults to the API key's sole organization with API access or the token's active organization. For OAuth/M2M tokens, any supplied organization_id must match the token's organization. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub organization_id: Option, +} + +impl OtfVolumeRequest { + /// Creates a new `OtfVolumeRequest` from its required fields, leaving every optional one unset. + /// + /// Pair it with struct-update syntax to set only the optionals you need: + /// `OtfVolumeRequest { regions: Some(…), ..OtfVolumeRequest::new(keyword, matching_type, start_date, end_date) }`. + pub fn new( + keyword: impl Into, + matching_type: OtfVolumeRequestMatchingType, + start_date: chrono::NaiveDate, + end_date: chrono::NaiveDate, + ) -> Self { + Self { + keyword: keyword.into(), + matching_type, + start_date, + end_date, + regions: None, + platforms: None, + organization_id: None, + } + } +} + +/// Response wrapper with metadata and data array. +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct OtfVolumeResponse { + /// Metadata for volume query response. + /// truncated indicates if results hit MAX_ROWS limit. + pub info: VolumeResponseInfo, + pub data: Vec, +} + +/// Metadata for volume query response. +/// truncated indicates if results hit MAX_ROWS limit. +/// +/// Response-only model, marked `#[non_exhaustive]`: fields may be added +/// additively in future versions without a breaking release. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[non_exhaustive] +pub struct VolumeResponseInfo { + pub total_rows: i64, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub truncated: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub query: Option, +} + +/// Match on `as_str()` (or build one with `From<&str>`) when you need a value +/// this SDK version does not know. Matching `Unknown(_)` directly is an +/// anti-pattern: that arm silently stops matching once a future SDK release +/// adds the real variant. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OtfVolumeRequestMatchingType { + #[serde(rename = "exact_match")] + ExactMatch, + #[serde(rename = "phrase_match")] + PhraseMatch, + /// A value not known to this version of the SDK, preserved verbatim. + #[serde(untagged)] + Unknown(String), +} + +impl OtfVolumeRequestMatchingType { + /// The wire value this variant serializes to. + pub fn as_str(&self) -> &str { + match self { + Self::ExactMatch => "exact_match", + Self::PhraseMatch => "phrase_match", + Self::Unknown(value) => value, + } + } +} + +impl std::fmt::Display for OtfVolumeRequestMatchingType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl From<&str> for OtfVolumeRequestMatchingType { + fn from(value: &str) -> Self { + match value { + "exact_match" => Self::ExactMatch, + "phrase_match" => Self::PhraseMatch, + other => Self::Unknown(other.to_owned()), + } + } +} + +/// Match on `as_str()` (or build one with `From<&str>`) when you need a value +/// this SDK version does not know. Matching `Unknown(_)` directly is an +/// anti-pattern: that arm silently stops matching once a future SDK release +/// adds the real variant. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OtfVolumeRequestPlatform { + #[serde(rename = "chatgpt.com")] + ChatgptCom, + #[serde(rename = "gemini.google.com")] + GeminiGoogleCom, + #[serde(rename = "perplexity.ai")] + PerplexityAi, + /// A value not known to this version of the SDK, preserved verbatim. + #[serde(untagged)] + Unknown(String), +} + +impl OtfVolumeRequestPlatform { + /// The wire value this variant serializes to. + pub fn as_str(&self) -> &str { + match self { + Self::ChatgptCom => "chatgpt.com", + Self::GeminiGoogleCom => "gemini.google.com", + Self::PerplexityAi => "perplexity.ai", + Self::Unknown(value) => value, + } + } +} + +impl std::fmt::Display for OtfVolumeRequestPlatform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl From<&str> for OtfVolumeRequestPlatform { + fn from(value: &str) -> Self { + match value { + "chatgpt.com" => Self::ChatgptCom, + "gemini.google.com" => Self::GeminiGoogleCom, + "perplexity.ai" => Self::PerplexityAi, + other => Self::Unknown(other.to_owned()), + } + } +} diff --git a/src/models/web_search_results.rs b/src/models/web_search_results.rs index 9391970..a142c1d 100644 --- a/src/models/web_search_results.rs +++ b/src/models/web_search_results.rs @@ -37,12 +37,15 @@ pub struct StreamWebSearchResultsQuery { /// #[serde(default, skip_serializing_if = "Option::is_none")] pub order_by: Option>, + /// Offset-based pagination parameters. #[serde(default, skip_serializing_if = "Option::is_none")] pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the web search results report. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -102,8 +105,10 @@ pub struct WebSearchResultsQuery { pub pagination: Option, pub category_id: String, /// Start date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub start_date: chrono::DateTime, /// End date for the report. Accepts formats: YYYY-MM-DD, YYYY-MM-DD HH:MM, or full ISO timestamp. + #[serde(deserialize_with = "crate::datetime::deserialize")] pub end_date: chrono::DateTime, /// List of filters to apply to the web search results report. #[serde(default, skip_serializing_if = "Option::is_none")] diff --git a/src/models/youtube.rs b/src/models/youtube.rs index 12b6b0d..75b4b73 100644 --- a/src/models/youtube.rs +++ b/src/models/youtube.rs @@ -39,7 +39,11 @@ pub struct YoutubeChannelRow { /// Distinct videos of this channel that were cited. pub videos: i64, /// Share of every YouTube citation in the window (attributed and unattributed alike), or the period when `interval` is set, regardless of `source_types`. An unnarrowed complete ranking sums to slightly less than 1 because unattributed citations cannot appear in channel rows; a narrowed ranking sums to its slice's share. + #[serde(serialize_with = "crate::number::serialize")] pub citation_share: f64, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Channel report metadata, including effective paging and grouping settings. @@ -79,6 +83,9 @@ pub struct YoutubeChannelsInfo { pub interval: Option, /// Echoed dimensions that define a row. Channel reports echo `["channel"]` when group_by is omitted. pub group_by: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Channel and category rollups. @@ -209,6 +216,9 @@ pub struct YoutubeSummaryInfo { pub category_id: String, /// All five YouTube source types; this endpoint has no source_types request field and cannot be narrowed. pub source_types: Vec, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// One object describing the window. @@ -290,7 +300,11 @@ pub struct YoutubeVideoRow { /// Citations attributed to this video. pub count: i64, /// Share of every YouTube citation in the window (attributed and unattributed alike), regardless of `source_types`. Shares sum to at most 1, reaching about 1 only with `attribution="all"` and no `source_types` filter; a narrowed ranking sums to its slice's share. + #[serde(serialize_with = "crate::number::serialize")] pub citation_share: f64, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Video report metadata, including effective paging and attribution settings. @@ -328,6 +342,9 @@ pub struct YoutubeVideosInfo { pub limit: i64, /// Effective video attribution mode. pub attribution: YoutubeVideosInfoAttribution, + /// Additional properties not captured by the named fields. + #[serde(flatten)] + pub additional_properties: std::collections::HashMap, } /// Cited videos, optionally narrowed to specific channels. diff --git a/src/number.rs b/src/number.rs new file mode 100644 index 0000000..903776f --- /dev/null +++ b/src/number.rs @@ -0,0 +1,226 @@ +//! Integer-preserving serialization for `type: number` schemas. +//! +//! OpenAPI's `number` maps to `f64`, and serde_json renders `100.0f64` as +//! `100.0`. That is a valid JSON number and numerically identical to `100`, but +//! it is not what the spec's own examples show, not what the Go and TypeScript +//! SDKs send, and some APIs reject the decimal form for an integer-valued field. +//! +//! These helpers render an integral value as a JSON integer and leave everything +//! else on serde_json's own float rendering. They are wired in per field via +//! `#[serde(serialize_with = …)]` — never as a global formatter — so only values +//! the document actually typed `number` are reshaped. A caller's free-form +//! `serde_json::Value` payload and an untagged union's float arm both keep their +//! exact rendering. +//! +//! Deserialization is untouched: `f64`'s own impl already accepts both `100` and +//! `100.0`. + +/// Largest magnitude an `f64` represents as an exact integer (2^53). +/// +/// Past this, consecutive integers are no longer all representable, so `as i64` +/// would print a value the `f64` does not actually hold. +const EXACT_INTEGER_LIMIT: f64 = 9_007_199_254_740_992.0; + +/// Renders an integral value as a JSON integer, everything else as a float. +/// +/// A non-finite value falls through to serde_json, which renders it as `null` — +/// the same thing it did before this module existed. +fn write(value: f64, serializer: S) -> Result { + if value.is_finite() && value.fract() == 0.0 && value.abs() <= EXACT_INTEGER_LIMIT { + // `-0.0` normalizes to `0`; JSON has no signed zero to preserve. + return serializer.serialize_i64(value as i64); + } + serializer.serialize_f64(value) +} + +/// One value, so the container helpers below can lean on serde's own `Option`, +/// sequence and map serialization instead of reimplementing each. +struct Integral(f64); + +impl serde::Serialize for Integral { + fn serialize(&self, serializer: S) -> Result { + write(self.0, serializer) + } +} + +/// A slice of values, each rendered through `Integral`. +struct Seq<'a>(&'a [f64]); + +impl serde::Serialize for Seq<'_> { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_seq(self.0.iter().copied().map(Integral)) + } +} + +/// A map of values, each rendered through `Integral`. +struct Map<'a>(&'a std::collections::HashMap); + +impl serde::Serialize for Map<'_> { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_map(self.0.iter().map(|(key, value)| (key, Integral(*value)))) + } +} + +/// `#[serde(serialize_with = "crate::number::serialize")]` for a bare field. +pub fn serialize(value: &f64, serializer: S) -> Result { + write(*value, serializer) +} + +/// The `Option<_>` field shape. +pub mod option { + use serde::Serialize as _; + + /// `#[serde(serialize_with = "crate::number::option::serialize")]`. + pub fn serialize(value: &Option, serializer: S) -> Result { + value.map(super::Integral).serialize(serializer) + } +} + +/// The `Vec<_>` field shape. +pub mod vec { + use serde::Serialize as _; + + /// `#[serde(serialize_with = "crate::number::vec::serialize")]`. + /// + /// Takes `&[f64]`, not `&Vec`: `clippy::ptr_arg` is denied by the + /// generated CI, and serde's call site deref-coerces either way. + pub fn serialize(value: &[f64], serializer: S) -> Result { + super::Seq(value).serialize(serializer) + } +} + +/// The `Option>` field shape. +pub mod option_vec { + use serde::Serialize as _; + + /// `#[serde(serialize_with = "crate::number::option_vec::serialize")]`. + pub fn serialize(value: &Option>, serializer: S) -> Result { + value.as_deref().map(super::Seq).serialize(serializer) + } +} + +/// The `HashMap` field shape. +pub mod map { + use serde::Serialize as _; + + /// `#[serde(serialize_with = "crate::number::map::serialize")]`. + pub fn serialize( + value: &std::collections::HashMap, + serializer: S, + ) -> Result { + super::Map(value).serialize(serializer) + } +} + +/// The `Option>` field shape. +pub mod option_map { + use serde::Serialize as _; + + /// `#[serde(serialize_with = "crate::number::option_map::serialize")]`. + pub fn serialize( + value: &Option>, + serializer: S, + ) -> Result { + value.as_ref().map(super::Map).serialize(serializer) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, serde::Serialize)] + struct Wrapper { + #[serde(serialize_with = "crate::number::serialize")] + v: f64, + } + + #[derive(Debug, serde::Serialize)] + struct Containers { + #[serde(serialize_with = "crate::number::option::serialize")] + o: Option, + #[serde(serialize_with = "crate::number::vec::serialize")] + l: Vec, + #[serde(serialize_with = "crate::number::map::serialize")] + m: std::collections::HashMap, + } + + fn encode(value: f64) -> String { + serde_json::to_string(&Wrapper { v: value }).expect("encodes") + } + + #[test] + fn integral_values_render_without_a_fractional_part() { + assert_eq!(encode(100.0), r#"{"v":100}"#); + assert_eq!(encode(0.0), r#"{"v":0}"#); + // JSON has no signed zero, so `-0.0` is just `0`. + assert_eq!(encode(-0.0), r#"{"v":0}"#); + assert_eq!(encode(-100.0), r#"{"v":-100}"#); + } + + #[test] + fn genuinely_fractional_values_are_untouched() { + assert_eq!(encode(1.5), r#"{"v":1.5}"#); + assert_eq!(encode(0.1), r#"{"v":0.1}"#); + } + + #[test] + fn values_past_exact_integer_range_stay_floats() { + // `as i64` stops being lossless past 2^53, so the float rendering is the + // only one that still describes the value the f64 actually holds. Compared + // against serde_json's own rendering rather than a pinned literal, because + // "unchanged" is the claim; the `e` check keeps that from passing + // vacuously if both sides ever became integers. + for value in [1e300, EXACT_INTEGER_LIMIT * 2.0] { + let encoded = encode(value); + assert_eq!(encoded, format!(r#"{{"v":{}}}"#, serde_json::json!(value))); + assert!(encoded.contains('e'), "{encoded}"); + } + } + + #[test] + fn non_finite_values_keep_serde_jsons_own_rendering() { + assert_eq!(encode(f64::NAN), r#"{"v":null}"#); + assert_eq!(encode(f64::INFINITY), r#"{"v":null}"#); + } + + #[test] + fn containers_reshape_every_element() { + let mut m = std::collections::HashMap::new(); + m.insert("fee".to_string(), 25.0); + let encoded = serde_json::to_string(&Containers { + o: Some(0.0), + l: vec![100.0, 1.5], + m, + }) + .expect("encodes"); + + assert!(encoded.contains(r#""o":0"#), "{encoded}"); + assert!(encoded.contains(r#""l":[100,1.5]"#), "{encoded}"); + assert!(encoded.contains(r#""fee":25"#), "{encoded}"); + } + + #[test] + fn an_unset_optional_still_serializes_as_null() { + let encoded = serde_json::to_string(&Containers { + o: None, + l: Vec::new(), + m: std::collections::HashMap::new(), + }) + .expect("encodes"); + + assert!(encoded.contains(r#""o":null"#), "{encoded}"); + } + + #[test] + fn an_integer_rendering_still_decodes_back_into_an_f64() { + // The whole point: the reshaped payload must remain readable by the same + // model that produced it. + #[derive(serde::Deserialize)] + struct Read { + v: f64, + } + let decoded: Read = serde_json::from_str(&encode(100.0)).expect("decodes"); + assert_eq!(decoded.v, 100.0); + } +} diff --git a/src/resources/intents.rs b/src/resources/intents.rs new file mode 100644 index 0000000..8c54a56 --- /dev/null +++ b/src/resources/intents.rs @@ -0,0 +1,102 @@ +// Code generated by Scalar SDK Generator. DO NOT EDIT. +//! The `intents` resource. + +#[derive(Clone)] +pub struct IntentsResource { + client: crate::client::ProfoundClient, +} + +impl IntentsResource { + pub(crate) fn new(client: crate::client::ProfoundClient) -> Self { + Self { client } + } + + /// Get Keyword Intent Shares + pub fn on_the_fly(&self, body: crate::models::OtfIntentSharesQuery) -> OnTheFlyRequestBuilder { + OnTheFlyRequestBuilder::new(self.client.clone(), body) + } +} + +/// Intent shares for one keyword across the requested cohort weeks. +/// +/// Shares are fractions from 0 to 1. Cohorts with two or fewer matching users +/// are omitted for privacy. Doesn't use the daily keyword allowance. +#[must_use = "a request builder does nothing until `.send().await` is called"] +pub struct OnTheFlyRequestBuilder { + client: crate::client::ProfoundClient, + body: crate::models::OtfIntentSharesQuery, + idempotency_key: Option, + timeout: Option, + max_retries: Option, +} + +impl OnTheFlyRequestBuilder { + fn new(client: crate::client::ProfoundClient, body: crate::models::OtfIntentSharesQuery) -> Self { + Self { + client, + body, + idempotency_key: None, + timeout: None, + max_retries: None, + } + } + + /// Sets an idempotency key so this request can be safely retried. + pub fn idempotency_key(mut self, value: impl Into) -> Self { + self.idempotency_key = Some(value.into()); + self + } + + /// Overrides the client's request deadline for this request only. + /// It bounds each attempt up to response headers, then the buffered + /// body read, so total wall time can exceed the value set here. A + /// streamed body is never deadline-killed; see `ClientBuilder::timeout`. + pub fn timeout(mut self, timeout: std::time::Duration) -> Self { + self.timeout = Some(timeout); + self + } + + /// Overrides the client's maximum retry attempts for this request only. + pub fn max_retries(mut self, max_retries: u32) -> Self { + self.max_retries = Some(max_retries); + self + } + + /// Sends the request and returns the decoded response. + /// + /// # Errors + /// + /// - [`Error::Api`](crate::error::Error::Api) — the server answered with a + /// non-success status; the status, headers, request id, and decoded body are + /// preserved on the [`ApiError`](crate::error::ApiError). + /// - [`Error::Transport`](crate::error::Error::Transport) — the request never + /// produced a response (connection failure, or the deadline elapsed). + /// - [`Error::Config`](crate::error::Error::Config) — a parameter value could not + /// be rendered into the request. + /// - [`Error::Serde`](crate::error::Error::Serde) — the response body did not + /// match the generated model. + pub async fn send(self) -> Result { + let path = "/v2/prompt-volumes/intents/on-the-fly".to_string(); + let query: Vec<(String, String)> = Vec::new(); + let mut headers: Vec<(String, String)> = Vec::new(); + if let Some(key) = self.idempotency_key { + headers.push(("Idempotency-Key".to_string(), key)); + } + let body = Some(&self.body); + let overrides = crate::client::RequestOverrides { + timeout: self.timeout, + max_retries: self.max_retries, + }; + self.client + .send::( + http::Method::POST, + &path, + &query, + &headers, + body, + true, + overrides, + ) + .await + } +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 24f40a0..3551657 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -13,6 +13,7 @@ pub mod factcheck; pub mod folders; pub mod generations; pub mod integrations; +pub mod intents; pub mod knowledge_bases; pub mod knowledge_bases_documents; pub mod node_types; @@ -20,12 +21,14 @@ pub mod openai_ads; pub mod optimization; pub mod organizations; pub mod projects; +pub mod prompt_volumes; pub mod prompts; pub mod reports; pub mod runs; pub mod shopping; pub mod social; pub mod tasks; +pub mod volume; pub mod web_search_results; pub mod youtube; @@ -69,4 +72,8 @@ impl crate::client::ProfoundClient { pub fn ads(&self) -> crate::resources::ads::AdsResource { crate::resources::ads::AdsResource::new(self.clone()) } + + pub fn prompt_volumes(&self) -> crate::resources::prompt_volumes::PromptVolumesResource { + crate::resources::prompt_volumes::PromptVolumesResource::new(self.clone()) + } } diff --git a/src/resources/prompt_volumes.rs b/src/resources/prompt_volumes.rs new file mode 100644 index 0000000..8cb8935 --- /dev/null +++ b/src/resources/prompt_volumes.rs @@ -0,0 +1,21 @@ +// Code generated by Scalar SDK Generator. DO NOT EDIT. +//! The `promptVolumes` resource. + +#[derive(Clone)] +pub struct PromptVolumesResource { + client: crate::client::ProfoundClient, +} + +impl PromptVolumesResource { + pub(crate) fn new(client: crate::client::ProfoundClient) -> Self { + Self { client } + } + + pub fn volume(&self) -> crate::resources::volume::VolumeResource { + crate::resources::volume::VolumeResource::new(self.client.clone()) + } + + pub fn intents(&self) -> crate::resources::intents::IntentsResource { + crate::resources::intents::IntentsResource::new(self.client.clone()) + } +} diff --git a/src/resources/volume.rs b/src/resources/volume.rs new file mode 100644 index 0000000..cf65c0c --- /dev/null +++ b/src/resources/volume.rs @@ -0,0 +1,103 @@ +// Code generated by Scalar SDK Generator. DO NOT EDIT. +//! The `volume` resource. + +#[derive(Clone)] +pub struct VolumeResource { + client: crate::client::ProfoundClient, +} + +impl VolumeResource { + pub(crate) fn new(client: crate::client::ProfoundClient) -> Self { + Self { client } + } + + /// Get Keyword Volume + pub fn on_the_fly(&self, body: crate::models::OtfVolumeRequest) -> OnTheFlyRequestBuilder { + OnTheFlyRequestBuilder::new(self.client.clone(), body) + } +} + +/// Weekly and monthly volume projections for one keyword. +/// +/// Each organization can look up 1,000 distinct keywords per UTC day; over +/// the cap returns 429 with Retry-After. Slices with two or fewer users are +/// omitted for privacy. +#[must_use = "a request builder does nothing until `.send().await` is called"] +pub struct OnTheFlyRequestBuilder { + client: crate::client::ProfoundClient, + body: crate::models::OtfVolumeRequest, + idempotency_key: Option, + timeout: Option, + max_retries: Option, +} + +impl OnTheFlyRequestBuilder { + fn new(client: crate::client::ProfoundClient, body: crate::models::OtfVolumeRequest) -> Self { + Self { + client, + body, + idempotency_key: None, + timeout: None, + max_retries: None, + } + } + + /// Sets an idempotency key so this request can be safely retried. + pub fn idempotency_key(mut self, value: impl Into) -> Self { + self.idempotency_key = Some(value.into()); + self + } + + /// Overrides the client's request deadline for this request only. + /// It bounds each attempt up to response headers, then the buffered + /// body read, so total wall time can exceed the value set here. A + /// streamed body is never deadline-killed; see `ClientBuilder::timeout`. + pub fn timeout(mut self, timeout: std::time::Duration) -> Self { + self.timeout = Some(timeout); + self + } + + /// Overrides the client's maximum retry attempts for this request only. + pub fn max_retries(mut self, max_retries: u32) -> Self { + self.max_retries = Some(max_retries); + self + } + + /// Sends the request and returns the decoded response. + /// + /// # Errors + /// + /// - [`Error::Api`](crate::error::Error::Api) — the server answered with a + /// non-success status; the status, headers, request id, and decoded body are + /// preserved on the [`ApiError`](crate::error::ApiError). + /// - [`Error::Transport`](crate::error::Error::Transport) — the request never + /// produced a response (connection failure, or the deadline elapsed). + /// - [`Error::Config`](crate::error::Error::Config) — a parameter value could not + /// be rendered into the request. + /// - [`Error::Serde`](crate::error::Error::Serde) — the response body did not + /// match the generated model. + pub async fn send(self) -> Result { + let path = "/v2/prompt-volumes/volume/on-the-fly".to_string(); + let query: Vec<(String, String)> = Vec::new(); + let mut headers: Vec<(String, String)> = Vec::new(); + if let Some(key) = self.idempotency_key { + headers.push(("Idempotency-Key".to_string(), key)); + } + let body = Some(&self.body); + let overrides = crate::client::RequestOverrides { + timeout: self.timeout, + max_retries: self.max_retries, + }; + self.client + .send::( + http::Method::POST, + &path, + &query, + &headers, + body, + true, + overrides, + ) + .await + } +} diff --git a/tests/smoke.rs b/tests/smoke.rs index d6c7a71..451252e 100644 --- a/tests/smoke.rs +++ b/tests/smoke.rs @@ -248,7 +248,7 @@ async fn smoke() { .limit(10000) .cursor("example") .order_by(PromptOrderBy::CreatedAt) - .order_dir(OrderByDirection::Asc) + .order_dir(OrderByDirection::Desc) .analysis_type(vec![AnalysisType::Visibility]) .prompt_type(vec![PromptType::Visibility]) .status(vec!["active".to_string()]) @@ -657,8 +657,8 @@ async fn smoke() { end_date: "".to_string(), include: Some(vec![AnswersV2QueryInclude::RunId]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -715,8 +715,8 @@ async fn smoke() { end_date: "".to_string(), include: Some(vec![AnswersV2QueryInclude::RunId]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1393,8 +1393,8 @@ async fn smoke() { interval: Some(CitationsV2QueryInterval::Day), scope: Some(CitationsV2QueryScope::All), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1464,8 +1464,8 @@ async fn smoke() { assets: None, filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsVisibilitySortSpec { field: None }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1497,6 +1497,7 @@ async fn smoke() { end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -1536,14 +1537,15 @@ async fn smoke() { end_date: "".to_string(), comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), + source: Some(SentimentV2QuerySource::Response), group_by: Some(vec![SentimentV2QueryGroupBy::Date]), metrics: Some(vec![SentimentV2QueryMetric::PositiveSentiment]), interval: Some(SentimentV2QueryInterval::Day), filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpec { field: None, dir: None }), include_cited_websites: Some(false), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1612,8 +1614,8 @@ async fn smoke() { field: "".to_string(), dir: None, }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1741,8 +1743,8 @@ async fn smoke() { assets: None, filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsVisibilitySortSpec { field: None }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1799,8 +1801,8 @@ async fn smoke() { interval: Some(CitationsV2QueryInterval::Day), scope: Some(CitationsV2QueryScope::All), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1825,6 +1827,7 @@ async fn smoke() { end_date: "".to_string(), comparison_start_date: None, comparison_end_date: None, + source: None, group_by: None, metrics: None, interval: None, @@ -1857,14 +1860,15 @@ async fn smoke() { end_date: "".to_string(), comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), + source: Some(SentimentV2QuerySource::Response), group_by: Some(vec![SentimentV2QueryGroupBy::Date]), metrics: Some(vec![SentimentV2QueryMetric::PositiveSentiment]), interval: Some(SentimentV2QueryInterval::Day), filter: None, sort: Some(AppRoutesV2AnswerEngineInsightsReportsSentimentSortSpec { field: None, dir: None }), include_cited_websites: Some(false), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -1925,8 +1929,8 @@ async fn smoke() { field: "".to_string(), dir: None, }), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2122,8 +2126,8 @@ async fn smoke() { scope: Some(ShoppingBrandsV2QueryScope::Owned), assets: None, filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2190,8 +2194,8 @@ async fn smoke() { scope: Some(ShoppingBrandsV2QueryScope::Owned), assets: None, filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2261,8 +2265,8 @@ async fn smoke() { target_product: Some("x".to_string()), competitor_limit: Some(5), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2334,8 +2338,8 @@ async fn smoke() { target_product: Some("x".to_string()), competitor_limit: Some(5), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2399,8 +2403,8 @@ async fn smoke() { metrics: Some(vec![ShoppingMerchantsV2QueryMetric::MerchantShare]), interval: Some(ShoppingMerchantsV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2466,8 +2470,8 @@ async fn smoke() { metrics: Some(vec![ShoppingMerchantsV2QueryMetric::MerchantShare]), interval: Some(ShoppingMerchantsV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2531,8 +2535,8 @@ async fn smoke() { metrics: Some(vec![ShoppingTriggerRateV2QueryMetric::TotalRuns]), interval: Some(ShoppingTriggerRateV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2598,8 +2602,8 @@ async fn smoke() { metrics: Some(vec![ShoppingTriggerRateV2QueryMetric::TotalRuns]), interval: Some(ShoppingTriggerRateV2QueryInterval::Day), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -2630,6 +2634,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -2669,6 +2674,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -2708,6 +2714,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -2754,6 +2761,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -2848,6 +2856,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -2890,6 +2899,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -2932,6 +2942,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -2974,6 +2985,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3016,6 +3028,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3054,6 +3067,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3092,6 +3106,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3130,6 +3145,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3168,6 +3184,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3206,6 +3223,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3244,6 +3262,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3286,6 +3305,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3328,6 +3348,7 @@ async fn smoke() { comparison_start_date: None, comparison_end_date: None, category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: None, topic_ids: None, exclude_topic_ids: None, tag_ids: None, @@ -3368,6 +3389,7 @@ async fn smoke() { comparison_start_date: Some("".to_string()), comparison_end_date: Some("".to_string()), category_id: "7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string(), + languages: Some(vec!["".to_string()]), topic_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), exclude_topic_ids: Some(false), tag_ids: Some(vec!["7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()]), @@ -3479,8 +3501,8 @@ async fn smoke() { end_date: "".to_string(), group_by: Some(vec![FactcheckScoresQueryGroupBy::Date]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3539,8 +3561,8 @@ async fn smoke() { end_date: "".to_string(), group_by: Some(vec![FactcheckScoresQueryGroupBy::Date]), filter: None, - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3601,8 +3623,8 @@ async fn smoke() { group_by: Some(vec![FactcheckClaimsQueryGroupBy::Model]), filter: None, include: Some(vec![FactcheckClaimsQueryInclude::Theme]), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3665,8 +3687,8 @@ async fn smoke() { group_by: Some(vec![FactcheckClaimsQueryGroupBy::Model]), filter: None, include: Some(vec![FactcheckClaimsQueryInclude::Theme]), - limit: Some(0), - max_results: Some(0), + limit: Some(1), + max_results: Some(1), cursor: Some("".to_string()), }) .send() @@ -3728,7 +3750,7 @@ async fn smoke() { start_date: "".to_string(), end_date: "".to_string(), filter: None, - limit: Some(0), + limit: Some(1), cursor: Some("".to_string()), source_types: Some(vec![YoutubeChannelsQuerySourceType::Video]), group_by: Some(vec![YoutubeChannelsQueryGroupBy::Channel]), @@ -3789,7 +3811,7 @@ async fn smoke() { start_date: "".to_string(), end_date: "".to_string(), filter: None, - limit: Some(0), + limit: Some(1), cursor: Some("".to_string()), source_types: Some(vec![YoutubeVideosQuerySourceType::Video]), attribution: Some(YoutubeVideosQueryAttribution::Attributed), @@ -4200,7 +4222,7 @@ async fn smoke() { "7c9e6679-7425-40de-944b-e07fc1f90ae7", SearchKnowledgeBaseRequest { query: "x".to_string(), - top_k: 0, + top_k: 1, return_full_page: None, filters: None, }, @@ -4227,7 +4249,7 @@ async fn smoke() { "7c9e6679-7425-40de-944b-e07fc1f90ae7", SearchKnowledgeBaseRequest { query: "x".to_string(), - top_k: 0, + top_k: 1, return_full_page: Some(false), filters: Some(SearchKnowledgeBaseFilters { tags: None, @@ -4580,7 +4602,7 @@ async fn smoke() { id: "x".to_string(), name: "x".to_string(), mime_type: "x".to_string(), - size_bytes: 0, + size_bytes: 1, data_base64: "x".to_string(), }]), generation_context: Some(ProjectGenerationContext { @@ -4822,7 +4844,7 @@ async fn smoke() { brief: Some("x".to_string()), r#type: Some("x".to_string()), topic: Some("x".to_string()), - impact: Some(0), + impact: Some(1), reference_url: Some("x".to_string()), reference_label: Some("x".to_string()), position: Some(0), @@ -4912,7 +4934,7 @@ async fn smoke() { brief: Some("x".to_string()), r#type: Some("x".to_string()), topic: Some("x".to_string()), - impact: Some(0), + impact: Some(1), reference_url: Some("x".to_string()), reference_label: Some("x".to_string()), }, @@ -5312,6 +5334,116 @@ async fn smoke() { } } } + { + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .volume() + .on_the_fly(OtfVolumeRequest { + keyword: "".to_string(), + matching_type: OtfVolumeRequestMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + organization_id: None, + }) + .send() + .await?; + Ok(()) + } + .await; + if let Err(error) = result { + if is_smoke_failure(&error) { + failures.push(format!( + "{}: {error}", + "POST /v2/prompt-volumes/volume/on-the-fly [required params]" + )); + } + } + } + { + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .volume() + .on_the_fly(OtfVolumeRequest { + keyword: "".to_string(), + matching_type: OtfVolumeRequestMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: Some(vec!["".to_string()]), + platforms: Some(vec![OtfVolumeRequestPlatform::ChatgptCom]), + organization_id: Some("7c9e6679-7425-40de-944b-e07fc1f90ae7".to_string()), + }) + .send() + .await?; + Ok(()) + } + .await; + if let Err(error) = result { + if is_smoke_failure(&error) { + failures.push(format!( + "{}: {error}", + "POST /v2/prompt-volumes/volume/on-the-fly [all params]" + )); + } + } + } + { + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .intents() + .on_the_fly(OtfIntentSharesQuery { + keyword: "".to_string(), + matching_type: OtfIntentSharesQueryMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: None, + platforms: None, + }) + .send() + .await?; + Ok(()) + } + .await; + if let Err(error) = result { + if is_smoke_failure(&error) { + failures.push(format!( + "{}: {error}", + "POST /v2/prompt-volumes/intents/on-the-fly [required params]" + )); + } + } + } + { + let result: Result<(), Error> = async { + let _ = client + .prompt_volumes() + .intents() + .on_the_fly(OtfIntentSharesQuery { + keyword: "".to_string(), + matching_type: OtfIntentSharesQueryMatchingType::ExactMatch, + start_date: chrono::Utc::now().date_naive(), + end_date: chrono::Utc::now().date_naive(), + regions: Some(vec!["".to_string()]), + platforms: Some(vec![OtfIntentSharesQueryPlatform::ChatgptCom]), + }) + .send() + .await?; + Ok(()) + } + .await; + if let Err(error) = result { + if is_smoke_failure(&error) { + failures.push(format!( + "{}: {error}", + "POST /v2/prompt-volumes/intents/on-the-fly [all params]" + )); + } + } + } assert!(failures.is_empty(), "smoke failures:\n{}", failures.join("\n")); }