diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 65f558e..656a2ef 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.0.0" + ".": "2.1.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f52f430..6b4e1c4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 29 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-7dbd739a81e6aaf7fc7ef1858606e7d572d309af55082a66431d9292e655e66f.yml -openapi_spec_hash: a6374d5d718a7bd9d3d54fb657e2bd9d +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-51ebe668b198157ccb18984017dd2fe35f7675220e8b933b03325b3c459ef2a6.yml +openapi_spec_hash: b3ed94bd6b59d4e32fc2c5aa8187d6b2 config_hash: ff35e224e809656528c44163aa41bebd diff --git a/CHANGELOG.md b/CHANGELOG.md index 8122751..ee81312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 2.1.0 (2026-07-08) + +Full Changelog: [v2.0.0...v2.1.0](https://github.com/context-dot-dev/context-php-sdk/compare/v2.0.0...v2.1.0) + +### Features + +* **api:** api update ([efb4692](https://github.com/context-dot-dev/context-php-sdk/commit/efb469262cfa86a8b362131a90c0ccacabf3e7c9)) +* **api:** api update ([5e090db](https://github.com/context-dot-dev/context-php-sdk/commit/5e090db2c8b5430cf5818984c928831952dcbb32)) +* **api:** api update ([3503b7b](https://github.com/context-dot-dev/context-php-sdk/commit/3503b7b911e5e6992406040ecb82c6d8f7711243)) +* **api:** api update ([5b92377](https://github.com/context-dot-dev/context-php-sdk/commit/5b9237711f3ad827a51e6166d71e69fab8fd37dd)) + ## 2.0.0 (2026-07-06) Full Changelog: [v1.3.0...v2.0.0](https://github.com/context-dot-dev/context-php-sdk/compare/v1.3.0...v2.0.0) diff --git a/README.md b/README.md index 817bc6b..270911f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ The REST API documentation can be found on [docs.context.dev](https://docs.conte ``` -composer require "context-dev/context-dev-php 2.0.0" +composer require "context-dev/context-dev-php 2.1.0" ``` diff --git a/src/Brand/BrandRetrieveParams.php b/src/Brand/BrandRetrieveParams.php index e32f18d..4d98ff9 100644 --- a/src/Brand/BrandRetrieveParams.php +++ b/src/Brand/BrandRetrieveParams.php @@ -13,7 +13,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, or transaction descriptor. + * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, transaction descriptor, or direct URL. Note: `by_direct_url` fetches brand data only from the provided URL — not from the entire internet. * * @see ContextDev\Services\BrandService::retrieve() * @@ -29,6 +29,7 @@ * email: string, * ticker: string, * tickerExchange?: string|null, + * directURL: string, * transactionInfo: string, * city?: string|null, * highConfidenceOnly?: bool|null, @@ -108,6 +109,12 @@ final class BrandRetrieveParams implements BaseModel #[Optional('ticker_exchange')] public ?string $tickerExchange; + /** + * Full http(s) URL to fetch brand data from (e.g., 'https://stripe.com/enterprise'). Only this URL is fetched — not the entire internet. + */ + #[Required('direct_url')] + public string $directURL; + /** * Transaction information to identify the brand. */ @@ -149,6 +156,7 @@ final class BrandRetrieveParams implements BaseModel * name: ..., * email: ..., * ticker: ..., + * directURL: ..., * transactionInfo: ..., * ) * ``` @@ -162,6 +170,7 @@ final class BrandRetrieveParams implements BaseModel * ->withName(...) * ->withEmail(...) * ->withTicker(...) + * ->withDirectURL(...) * ->withTransactionInfo(...) * ``` */ @@ -184,6 +193,7 @@ public static function with( string $name, string $email, string $ticker, + string $directURL, string $transactionInfo, ForceLanguage|string|null $forceLanguage = null, ?int $maxAgeMs = null, @@ -203,6 +213,7 @@ public static function with( $self['name'] = $name; $self['email'] = $email; $self['ticker'] = $ticker; + $self['directURL'] = $directURL; $self['transactionInfo'] = $transactionInfo; null !== $forceLanguage && $self['forceLanguage'] = $forceLanguage; @@ -342,6 +353,17 @@ public function withTickerExchange(string $tickerExchange): self return $self; } + /** + * Full http(s) URL to fetch brand data from (e.g., 'https://stripe.com/enterprise'). Only this URL is fetched — not the entire internet. + */ + public function withDirectURL(string $directURL): self + { + $self = clone $this; + $self['directURL'] = $directURL; + + return $self; + } + /** * Transaction information to identify the brand. */ diff --git a/src/Monitors/MonitorCreateParams/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorCreateParams/ChangeDetection/MonitorsSemanticChangeDetection.php index 8297408..27eaf06 100644 --- a/src/Monitors/MonitorCreateParams/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorCreateParams/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorCreateParams/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorCreateParams/Target/MonitorsExtractTarget.php index 0590362..15ded69 100644 --- a/src/Monitors/MonitorCreateParams/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorCreateParams/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorCreateParams/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorCreateParams/Target/MonitorsSitemapTarget.php index 92e7c01..a16f418 100644 --- a/src/Monitors/MonitorCreateParams/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorCreateParams/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/Monitors/MonitorGetChangeResponse.php b/src/Monitors/MonitorGetChangeResponse.php index 44f7b82..bc39e1a 100644 --- a/src/Monitors/MonitorGetChangeResponse.php +++ b/src/Monitors/MonitorGetChangeResponse.php @@ -40,7 +40,6 @@ * importance?: null|Importance|value-of, * matchedURLCount?: int|null, * matchedURLs?: list|null, - * query?: string|null, * removedURLCount?: int|null, * removedURLs?: list|null, * tags?: list|null, @@ -136,9 +135,6 @@ final class MonitorGetChangeResponse implements BaseModel #[Optional('matched_urls', list: 'string')] public ?array $matchedURLs; - #[Optional] - public ?string $query; - #[Optional('removed_url_count')] public ?int $removedURLCount; @@ -234,7 +230,6 @@ public static function with( Importance|string|null $importance = null, ?int $matchedURLCount = null, ?array $matchedURLs = null, - ?string $query = null, ?int $removedURLCount = null, ?array $removedURLs = null, ?array $tags = null, @@ -262,7 +257,6 @@ public static function with( null !== $importance && $self['importance'] = $importance; null !== $matchedURLCount && $self['matchedURLCount'] = $matchedURLCount; null !== $matchedURLs && $self['matchedURLs'] = $matchedURLs; - null !== $query && $self['query'] = $query; null !== $removedURLCount && $self['removedURLCount'] = $removedURLCount; null !== $removedURLs && $self['removedURLs'] = $removedURLs; null !== $tags && $self['tags'] = $tags; @@ -464,14 +458,6 @@ public function withMatchedURLs(array $matchedURLs): self return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - public function withRemovedURLCount(int $removedURLCount): self { $self = clone $this; diff --git a/src/Monitors/MonitorGetResponse.php b/src/Monitors/MonitorGetResponse.php index 5e0fba5..c0a3e51 100644 --- a/src/Monitors/MonitorGetResponse.php +++ b/src/Monitors/MonitorGetResponse.php @@ -8,6 +8,9 @@ use ContextDev\Core\Attributes\Required; use ContextDev\Core\Concerns\SdkModel; use ContextDev\Core\Contracts\BaseModel; +use ContextDev\Monitors\MonitorGetResponse\Baseline\MonitorsExtractBaseline; +use ContextDev\Monitors\MonitorGetResponse\Baseline\MonitorsPageBaseline; +use ContextDev\Monitors\MonitorGetResponse\Baseline\MonitorsSitemapBaseline; use ContextDev\Monitors\MonitorGetResponse\ChangeDetection; use ContextDev\Monitors\MonitorGetResponse\ChangeDetection\MonitorsExactChangeDetection; use ContextDev\Monitors\MonitorGetResponse\ChangeDetection\MonitorsSemanticChangeDetection; @@ -26,9 +29,11 @@ * * @phpstan-import-type ChangeDetectionVariants from \ContextDev\Monitors\MonitorGetResponse\ChangeDetection * @phpstan-import-type TargetVariants from \ContextDev\Monitors\MonitorGetResponse\Target + * @phpstan-import-type BaselineVariants from \ContextDev\Monitors\MonitorGetResponse\Baseline * @phpstan-import-type ChangeDetectionShape from \ContextDev\Monitors\MonitorGetResponse\ChangeDetection * @phpstan-import-type ScheduleShape from \ContextDev\Monitors\MonitorGetResponse\Schedule * @phpstan-import-type TargetShape from \ContextDev\Monitors\MonitorGetResponse\Target + * @phpstan-import-type BaselineShape from \ContextDev\Monitors\MonitorGetResponse\Baseline * @phpstan-import-type LastErrorShape from \ContextDev\Monitors\MonitorGetResponse\LastError * @phpstan-import-type WebhookShape from \ContextDev\Monitors\MonitorGetResponse\Webhook * @@ -42,6 +47,7 @@ * status: Status|value-of, * target: TargetShape, * updatedAt: \DateTimeInterface, + * baseline?: BaselineShape|null, * lastChangeAt?: \DateTimeInterface|null, * lastError?: null|LastError|LastErrorShape, * lastRunAt?: \DateTimeInterface|null, @@ -105,6 +111,14 @@ final class MonitorGetResponse implements BaseModel #[Required('updated_at')] public \DateTimeInterface $updatedAt; + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @var BaselineVariants|null $baseline + */ + #[Optional(nullable: true)] + public MonitorsPageBaseline|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline; + #[Optional('last_change_at', nullable: true)] public ?\DateTimeInterface $lastChangeAt; @@ -182,6 +196,7 @@ public function __construct() * @param Schedule|ScheduleShape $schedule * @param Status|value-of $status * @param TargetShape $target + * @param BaselineShape|null $baseline * @param LastError|LastErrorShape|null $lastError * @param list|null $tags * @param Webhook|WebhookShape|null $webhook @@ -196,6 +211,7 @@ public static function with( Status|string $status, MonitorsPageTarget|array|MonitorsSitemapTarget|MonitorsExtractTarget $target, \DateTimeInterface $updatedAt, + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline = null, ?\DateTimeInterface $lastChangeAt = null, LastError|array|null $lastError = null, ?\DateTimeInterface $lastRunAt = null, @@ -215,6 +231,7 @@ public static function with( $self['target'] = $target; $self['updatedAt'] = $updatedAt; + null !== $baseline && $self['baseline'] = $baseline; null !== $lastChangeAt && $self['lastChangeAt'] = $lastChangeAt; null !== $lastError && $self['lastError'] = $lastError; null !== $lastRunAt && $self['lastRunAt'] = $lastRunAt; @@ -324,6 +341,20 @@ public function withUpdatedAt(\DateTimeInterface $updatedAt): self return $self; } + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @param BaselineShape|null $baseline + */ + public function withBaseline( + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline, + ): self { + $self = clone $this; + $self['baseline'] = $baseline; + + return $self; + } + public function withLastChangeAt(?\DateTimeInterface $lastChangeAt): self { $self = clone $this; diff --git a/src/Monitors/MonitorGetResponse/Baseline.php b/src/Monitors/MonitorGetResponse/Baseline.php new file mode 100644 index 0000000..f5e08b9 --- /dev/null +++ b/src/Monitors/MonitorGetResponse/Baseline.php @@ -0,0 +1,39 @@ +|array + */ + public static function variants(): array + { + return [ + MonitorsPageBaseline::class, + MonitorsSitemapBaseline::class, + MonitorsExtractBaseline::class, + ]; + } +} diff --git a/src/Monitors/MonitorGetResponse/Baseline/MonitorsExtractBaseline.php b/src/Monitors/MonitorGetResponse/Baseline/MonitorsExtractBaseline.php new file mode 100644 index 0000000..26fc806 --- /dev/null +++ b/src/Monitors/MonitorGetResponse/Baseline/MonitorsExtractBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsExtractBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + #[Required] + public mixed $data; + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @var list $urlsAnalyzed + */ + #[Required('urls_analyzed', list: 'string')] + public array $urlsAnalyzed; + + /** + * `new MonitorsExtractBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsExtractBaseline::with(capturedAt: ..., data: ..., urlsAnalyzed: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsExtractBaseline) + * ->withCapturedAt(...) + * ->withData(...) + * ->withURLsAnalyzed(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urlsAnalyzed + */ + public static function with( + \DateTimeInterface $capturedAt, + mixed $data, + array $urlsAnalyzed + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['data'] = $data; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + public function withData(mixed $data): self + { + $self = clone $this; + $self['data'] = $data; + + return $self; + } + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @param list $urlsAnalyzed + */ + public function withURLsAnalyzed(array $urlsAnalyzed): self + { + $self = clone $this; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } +} diff --git a/src/Monitors/MonitorGetResponse/Baseline/MonitorsPageBaseline.php b/src/Monitors/MonitorGetResponse/Baseline/MonitorsPageBaseline.php new file mode 100644 index 0000000..dddacea --- /dev/null +++ b/src/Monitors/MonitorGetResponse/Baseline/MonitorsPageBaseline.php @@ -0,0 +1,92 @@ + */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The page's visible text as last observed. + */ + #[Required] + public string $text; + + /** + * `new MonitorsPageBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsPageBaseline::with(capturedAt: ..., text: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsPageBaseline)->withCapturedAt(...)->withText(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + \DateTimeInterface $capturedAt, + string $text + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['text'] = $text; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The page's visible text as last observed. + */ + public function withText(string $text): self + { + $self = clone $this; + $self['text'] = $text; + + return $self; + } +} diff --git a/src/Monitors/MonitorGetResponse/Baseline/MonitorsSitemapBaseline.php b/src/Monitors/MonitorGetResponse/Baseline/MonitorsSitemapBaseline.php new file mode 100644 index 0000000..aca2355 --- /dev/null +++ b/src/Monitors/MonitorGetResponse/Baseline/MonitorsSitemapBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsSitemapBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * Number of URLs in the baseline. + */ + #[Required('url_count')] + public int $urlCount; + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @var list $urls + */ + #[Required(list: 'string')] + public array $urls; + + /** + * `new MonitorsSitemapBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsSitemapBaseline::with(capturedAt: ..., urlCount: ..., urls: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsSitemapBaseline) + * ->withCapturedAt(...) + * ->withURLCount(...) + * ->withURLs(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urls + */ + public static function with( + \DateTimeInterface $capturedAt, + int $urlCount, + array $urls + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['urlCount'] = $urlCount; + $self['urls'] = $urls; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * Number of URLs in the baseline. + */ + public function withURLCount(int $urlCount): self + { + $self = clone $this; + $self['urlCount'] = $urlCount; + + return $self; + } + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @param list $urls + */ + public function withURLs(array $urls): self + { + $self = clone $this; + $self['urls'] = $urls; + + return $self; + } +} diff --git a/src/Monitors/MonitorGetResponse/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorGetResponse/ChangeDetection/MonitorsSemanticChangeDetection.php index c704604..e4ead61 100644 --- a/src/Monitors/MonitorGetResponse/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorGetResponse/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorGetResponse/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorGetResponse/Target/MonitorsExtractTarget.php index c5d50b5..609f9c4 100644 --- a/src/Monitors/MonitorGetResponse/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorGetResponse/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorGetResponse/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorGetResponse/Target/MonitorsSitemapTarget.php index 4ae0fca..28ee9ec 100644 --- a/src/Monitors/MonitorGetResponse/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorGetResponse/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/Monitors/MonitorListParams.php b/src/Monitors/MonitorListParams.php index 9f167e4..2119e05 100644 --- a/src/Monitors/MonitorListParams.php +++ b/src/Monitors/MonitorListParams.php @@ -55,7 +55,7 @@ final class MonitorListParams implements BaseModel public ?string $q; /** - * Comma-separated fields to search with `q`. Defaults to all of them. Note `query` only exists on semantic monitors. + * Comma-separated fields to search with `q`. Defaults to all of them. Note `instructions` only exists on extract monitors. * * @var list>|null $searchBy */ @@ -181,7 +181,7 @@ public function withQ(string $q): self } /** - * Comma-separated fields to search with `q`. Defaults to all of them. Note `query` only exists on semantic monitors. + * Comma-separated fields to search with `q`. Defaults to all of them. Note `instructions` only exists on extract monitors. * * @param list> $searchBy */ diff --git a/src/Monitors/MonitorListParams/SearchBy.php b/src/Monitors/MonitorListParams/SearchBy.php index e35ffc6..c9c728b 100644 --- a/src/Monitors/MonitorListParams/SearchBy.php +++ b/src/Monitors/MonitorListParams/SearchBy.php @@ -10,7 +10,7 @@ enum SearchBy: string case URL = 'url'; - case QUERY = 'query'; + case INSTRUCTIONS = 'instructions'; case TAGS = 'tags'; } diff --git a/src/Monitors/MonitorListResponse/Data.php b/src/Monitors/MonitorListResponse/Data.php index 9552664..ce057b7 100644 --- a/src/Monitors/MonitorListResponse/Data.php +++ b/src/Monitors/MonitorListResponse/Data.php @@ -8,6 +8,9 @@ use ContextDev\Core\Attributes\Required; use ContextDev\Core\Concerns\SdkModel; use ContextDev\Core\Contracts\BaseModel; +use ContextDev\Monitors\MonitorListResponse\Data\Baseline\MonitorsExtractBaseline; +use ContextDev\Monitors\MonitorListResponse\Data\Baseline\MonitorsPageBaseline; +use ContextDev\Monitors\MonitorListResponse\Data\Baseline\MonitorsSitemapBaseline; use ContextDev\Monitors\MonitorListResponse\Data\ChangeDetection; use ContextDev\Monitors\MonitorListResponse\Data\ChangeDetection\MonitorsExactChangeDetection; use ContextDev\Monitors\MonitorListResponse\Data\ChangeDetection\MonitorsSemanticChangeDetection; @@ -26,9 +29,11 @@ * * @phpstan-import-type ChangeDetectionVariants from \ContextDev\Monitors\MonitorListResponse\Data\ChangeDetection * @phpstan-import-type TargetVariants from \ContextDev\Monitors\MonitorListResponse\Data\Target + * @phpstan-import-type BaselineVariants from \ContextDev\Monitors\MonitorListResponse\Data\Baseline * @phpstan-import-type ChangeDetectionShape from \ContextDev\Monitors\MonitorListResponse\Data\ChangeDetection * @phpstan-import-type ScheduleShape from \ContextDev\Monitors\MonitorListResponse\Data\Schedule * @phpstan-import-type TargetShape from \ContextDev\Monitors\MonitorListResponse\Data\Target + * @phpstan-import-type BaselineShape from \ContextDev\Monitors\MonitorListResponse\Data\Baseline * @phpstan-import-type LastErrorShape from \ContextDev\Monitors\MonitorListResponse\Data\LastError * @phpstan-import-type WebhookShape from \ContextDev\Monitors\MonitorListResponse\Data\Webhook * @@ -42,6 +47,7 @@ * status: Status|value-of, * target: TargetShape, * updatedAt: \DateTimeInterface, + * baseline?: BaselineShape|null, * lastChangeAt?: \DateTimeInterface|null, * lastError?: null|LastError|LastErrorShape, * lastRunAt?: \DateTimeInterface|null, @@ -105,6 +111,14 @@ final class Data implements BaseModel #[Required('updated_at')] public \DateTimeInterface $updatedAt; + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @var BaselineVariants|null $baseline + */ + #[Optional(nullable: true)] + public MonitorsPageBaseline|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline; + #[Optional('last_change_at', nullable: true)] public ?\DateTimeInterface $lastChangeAt; @@ -182,6 +196,7 @@ public function __construct() * @param Schedule|ScheduleShape $schedule * @param Status|value-of $status * @param TargetShape $target + * @param BaselineShape|null $baseline * @param LastError|LastErrorShape|null $lastError * @param list|null $tags * @param Webhook|WebhookShape|null $webhook @@ -196,6 +211,7 @@ public static function with( Status|string $status, MonitorsPageTarget|array|MonitorsSitemapTarget|MonitorsExtractTarget $target, \DateTimeInterface $updatedAt, + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline = null, ?\DateTimeInterface $lastChangeAt = null, LastError|array|null $lastError = null, ?\DateTimeInterface $lastRunAt = null, @@ -215,6 +231,7 @@ public static function with( $self['target'] = $target; $self['updatedAt'] = $updatedAt; + null !== $baseline && $self['baseline'] = $baseline; null !== $lastChangeAt && $self['lastChangeAt'] = $lastChangeAt; null !== $lastError && $self['lastError'] = $lastError; null !== $lastRunAt && $self['lastRunAt'] = $lastRunAt; @@ -324,6 +341,20 @@ public function withUpdatedAt(\DateTimeInterface $updatedAt): self return $self; } + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @param BaselineShape|null $baseline + */ + public function withBaseline( + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline, + ): self { + $self = clone $this; + $self['baseline'] = $baseline; + + return $self; + } + public function withLastChangeAt(?\DateTimeInterface $lastChangeAt): self { $self = clone $this; diff --git a/src/Monitors/MonitorListResponse/Data/Baseline.php b/src/Monitors/MonitorListResponse/Data/Baseline.php new file mode 100644 index 0000000..d586497 --- /dev/null +++ b/src/Monitors/MonitorListResponse/Data/Baseline.php @@ -0,0 +1,39 @@ +|array + */ + public static function variants(): array + { + return [ + MonitorsPageBaseline::class, + MonitorsSitemapBaseline::class, + MonitorsExtractBaseline::class, + ]; + } +} diff --git a/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsExtractBaseline.php b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsExtractBaseline.php new file mode 100644 index 0000000..4fd75ef --- /dev/null +++ b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsExtractBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsExtractBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + #[Required] + public mixed $data; + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @var list $urlsAnalyzed + */ + #[Required('urls_analyzed', list: 'string')] + public array $urlsAnalyzed; + + /** + * `new MonitorsExtractBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsExtractBaseline::with(capturedAt: ..., data: ..., urlsAnalyzed: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsExtractBaseline) + * ->withCapturedAt(...) + * ->withData(...) + * ->withURLsAnalyzed(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urlsAnalyzed + */ + public static function with( + \DateTimeInterface $capturedAt, + mixed $data, + array $urlsAnalyzed + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['data'] = $data; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + public function withData(mixed $data): self + { + $self = clone $this; + $self['data'] = $data; + + return $self; + } + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @param list $urlsAnalyzed + */ + public function withURLsAnalyzed(array $urlsAnalyzed): self + { + $self = clone $this; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } +} diff --git a/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsPageBaseline.php b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsPageBaseline.php new file mode 100644 index 0000000..17ad750 --- /dev/null +++ b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsPageBaseline.php @@ -0,0 +1,92 @@ + */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The page's visible text as last observed. + */ + #[Required] + public string $text; + + /** + * `new MonitorsPageBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsPageBaseline::with(capturedAt: ..., text: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsPageBaseline)->withCapturedAt(...)->withText(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + \DateTimeInterface $capturedAt, + string $text + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['text'] = $text; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The page's visible text as last observed. + */ + public function withText(string $text): self + { + $self = clone $this; + $self['text'] = $text; + + return $self; + } +} diff --git a/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsSitemapBaseline.php b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsSitemapBaseline.php new file mode 100644 index 0000000..e319c53 --- /dev/null +++ b/src/Monitors/MonitorListResponse/Data/Baseline/MonitorsSitemapBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsSitemapBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * Number of URLs in the baseline. + */ + #[Required('url_count')] + public int $urlCount; + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @var list $urls + */ + #[Required(list: 'string')] + public array $urls; + + /** + * `new MonitorsSitemapBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsSitemapBaseline::with(capturedAt: ..., urlCount: ..., urls: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsSitemapBaseline) + * ->withCapturedAt(...) + * ->withURLCount(...) + * ->withURLs(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urls + */ + public static function with( + \DateTimeInterface $capturedAt, + int $urlCount, + array $urls + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['urlCount'] = $urlCount; + $self['urls'] = $urls; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * Number of URLs in the baseline. + */ + public function withURLCount(int $urlCount): self + { + $self = clone $this; + $self['urlCount'] = $urlCount; + + return $self; + } + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @param list $urls + */ + public function withURLs(array $urls): self + { + $self = clone $this; + $self['urls'] = $urls; + + return $self; + } +} diff --git a/src/Monitors/MonitorListResponse/Data/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorListResponse/Data/ChangeDetection/MonitorsSemanticChangeDetection.php index 9a6a468..6859c14 100644 --- a/src/Monitors/MonitorListResponse/Data/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorListResponse/Data/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorListResponse/Data/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorListResponse/Data/Target/MonitorsExtractTarget.php index c092e11..a88be1a 100644 --- a/src/Monitors/MonitorListResponse/Data/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorListResponse/Data/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorListResponse/Data/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorListResponse/Data/Target/MonitorsSitemapTarget.php index 04a020f..b16de60 100644 --- a/src/Monitors/MonitorListResponse/Data/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorListResponse/Data/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/Monitors/MonitorNewResponse.php b/src/Monitors/MonitorNewResponse.php index 1f40075..c61def5 100644 --- a/src/Monitors/MonitorNewResponse.php +++ b/src/Monitors/MonitorNewResponse.php @@ -8,6 +8,9 @@ use ContextDev\Core\Attributes\Required; use ContextDev\Core\Concerns\SdkModel; use ContextDev\Core\Contracts\BaseModel; +use ContextDev\Monitors\MonitorNewResponse\Baseline\MonitorsExtractBaseline; +use ContextDev\Monitors\MonitorNewResponse\Baseline\MonitorsPageBaseline; +use ContextDev\Monitors\MonitorNewResponse\Baseline\MonitorsSitemapBaseline; use ContextDev\Monitors\MonitorNewResponse\ChangeDetection; use ContextDev\Monitors\MonitorNewResponse\ChangeDetection\MonitorsExactChangeDetection; use ContextDev\Monitors\MonitorNewResponse\ChangeDetection\MonitorsSemanticChangeDetection; @@ -26,9 +29,11 @@ * * @phpstan-import-type ChangeDetectionVariants from \ContextDev\Monitors\MonitorNewResponse\ChangeDetection * @phpstan-import-type TargetVariants from \ContextDev\Monitors\MonitorNewResponse\Target + * @phpstan-import-type BaselineVariants from \ContextDev\Monitors\MonitorNewResponse\Baseline * @phpstan-import-type ChangeDetectionShape from \ContextDev\Monitors\MonitorNewResponse\ChangeDetection * @phpstan-import-type ScheduleShape from \ContextDev\Monitors\MonitorNewResponse\Schedule * @phpstan-import-type TargetShape from \ContextDev\Monitors\MonitorNewResponse\Target + * @phpstan-import-type BaselineShape from \ContextDev\Monitors\MonitorNewResponse\Baseline * @phpstan-import-type LastErrorShape from \ContextDev\Monitors\MonitorNewResponse\LastError * @phpstan-import-type WebhookShape from \ContextDev\Monitors\MonitorNewResponse\Webhook * @@ -42,6 +47,7 @@ * status: Status|value-of, * target: TargetShape, * updatedAt: \DateTimeInterface, + * baseline?: BaselineShape|null, * lastChangeAt?: \DateTimeInterface|null, * lastError?: null|LastError|LastErrorShape, * lastRunAt?: \DateTimeInterface|null, @@ -105,6 +111,14 @@ final class MonitorNewResponse implements BaseModel #[Required('updated_at')] public \DateTimeInterface $updatedAt; + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @var BaselineVariants|null $baseline + */ + #[Optional(nullable: true)] + public MonitorsPageBaseline|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline; + #[Optional('last_change_at', nullable: true)] public ?\DateTimeInterface $lastChangeAt; @@ -182,6 +196,7 @@ public function __construct() * @param Schedule|ScheduleShape $schedule * @param Status|value-of $status * @param TargetShape $target + * @param BaselineShape|null $baseline * @param LastError|LastErrorShape|null $lastError * @param list|null $tags * @param Webhook|WebhookShape|null $webhook @@ -196,6 +211,7 @@ public static function with( Status|string $status, MonitorsPageTarget|array|MonitorsSitemapTarget|MonitorsExtractTarget $target, \DateTimeInterface $updatedAt, + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline = null, ?\DateTimeInterface $lastChangeAt = null, LastError|array|null $lastError = null, ?\DateTimeInterface $lastRunAt = null, @@ -215,6 +231,7 @@ public static function with( $self['target'] = $target; $self['updatedAt'] = $updatedAt; + null !== $baseline && $self['baseline'] = $baseline; null !== $lastChangeAt && $self['lastChangeAt'] = $lastChangeAt; null !== $lastError && $self['lastError'] = $lastError; null !== $lastRunAt && $self['lastRunAt'] = $lastRunAt; @@ -324,6 +341,20 @@ public function withUpdatedAt(\DateTimeInterface $updatedAt): self return $self; } + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @param BaselineShape|null $baseline + */ + public function withBaseline( + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline, + ): self { + $self = clone $this; + $self['baseline'] = $baseline; + + return $self; + } + public function withLastChangeAt(?\DateTimeInterface $lastChangeAt): self { $self = clone $this; diff --git a/src/Monitors/MonitorNewResponse/Baseline.php b/src/Monitors/MonitorNewResponse/Baseline.php new file mode 100644 index 0000000..5b4f99a --- /dev/null +++ b/src/Monitors/MonitorNewResponse/Baseline.php @@ -0,0 +1,39 @@ +|array + */ + public static function variants(): array + { + return [ + MonitorsPageBaseline::class, + MonitorsSitemapBaseline::class, + MonitorsExtractBaseline::class, + ]; + } +} diff --git a/src/Monitors/MonitorNewResponse/Baseline/MonitorsExtractBaseline.php b/src/Monitors/MonitorNewResponse/Baseline/MonitorsExtractBaseline.php new file mode 100644 index 0000000..9ef584b --- /dev/null +++ b/src/Monitors/MonitorNewResponse/Baseline/MonitorsExtractBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsExtractBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + #[Required] + public mixed $data; + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @var list $urlsAnalyzed + */ + #[Required('urls_analyzed', list: 'string')] + public array $urlsAnalyzed; + + /** + * `new MonitorsExtractBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsExtractBaseline::with(capturedAt: ..., data: ..., urlsAnalyzed: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsExtractBaseline) + * ->withCapturedAt(...) + * ->withData(...) + * ->withURLsAnalyzed(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urlsAnalyzed + */ + public static function with( + \DateTimeInterface $capturedAt, + mixed $data, + array $urlsAnalyzed + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['data'] = $data; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + public function withData(mixed $data): self + { + $self = clone $this; + $self['data'] = $data; + + return $self; + } + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @param list $urlsAnalyzed + */ + public function withURLsAnalyzed(array $urlsAnalyzed): self + { + $self = clone $this; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } +} diff --git a/src/Monitors/MonitorNewResponse/Baseline/MonitorsPageBaseline.php b/src/Monitors/MonitorNewResponse/Baseline/MonitorsPageBaseline.php new file mode 100644 index 0000000..1dd9f40 --- /dev/null +++ b/src/Monitors/MonitorNewResponse/Baseline/MonitorsPageBaseline.php @@ -0,0 +1,92 @@ + */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The page's visible text as last observed. + */ + #[Required] + public string $text; + + /** + * `new MonitorsPageBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsPageBaseline::with(capturedAt: ..., text: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsPageBaseline)->withCapturedAt(...)->withText(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + \DateTimeInterface $capturedAt, + string $text + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['text'] = $text; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The page's visible text as last observed. + */ + public function withText(string $text): self + { + $self = clone $this; + $self['text'] = $text; + + return $self; + } +} diff --git a/src/Monitors/MonitorNewResponse/Baseline/MonitorsSitemapBaseline.php b/src/Monitors/MonitorNewResponse/Baseline/MonitorsSitemapBaseline.php new file mode 100644 index 0000000..a988c1f --- /dev/null +++ b/src/Monitors/MonitorNewResponse/Baseline/MonitorsSitemapBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsSitemapBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * Number of URLs in the baseline. + */ + #[Required('url_count')] + public int $urlCount; + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @var list $urls + */ + #[Required(list: 'string')] + public array $urls; + + /** + * `new MonitorsSitemapBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsSitemapBaseline::with(capturedAt: ..., urlCount: ..., urls: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsSitemapBaseline) + * ->withCapturedAt(...) + * ->withURLCount(...) + * ->withURLs(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urls + */ + public static function with( + \DateTimeInterface $capturedAt, + int $urlCount, + array $urls + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['urlCount'] = $urlCount; + $self['urls'] = $urls; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * Number of URLs in the baseline. + */ + public function withURLCount(int $urlCount): self + { + $self = clone $this; + $self['urlCount'] = $urlCount; + + return $self; + } + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @param list $urls + */ + public function withURLs(array $urls): self + { + $self = clone $this; + $self['urls'] = $urls; + + return $self; + } +} diff --git a/src/Monitors/MonitorNewResponse/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorNewResponse/ChangeDetection/MonitorsSemanticChangeDetection.php index 21c2a85..9b35b99 100644 --- a/src/Monitors/MonitorNewResponse/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorNewResponse/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorNewResponse/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorNewResponse/Target/MonitorsExtractTarget.php index d71e5bb..70d297c 100644 --- a/src/Monitors/MonitorNewResponse/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorNewResponse/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorNewResponse/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorNewResponse/Target/MonitorsSitemapTarget.php index bef89b1..f01e31b 100644 --- a/src/Monitors/MonitorNewResponse/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorNewResponse/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/Monitors/MonitorUpdateParams/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorUpdateParams/ChangeDetection/MonitorsSemanticChangeDetection.php index e97261c..a39d915 100644 --- a/src/Monitors/MonitorUpdateParams/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorUpdateParams/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorUpdateParams/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorUpdateParams/Target/MonitorsExtractTarget.php index 61bb6ff..84e8ce2 100644 --- a/src/Monitors/MonitorUpdateParams/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorUpdateParams/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorUpdateParams/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorUpdateParams/Target/MonitorsSitemapTarget.php index d2e5441..5da0cb2 100644 --- a/src/Monitors/MonitorUpdateParams/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorUpdateParams/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/Monitors/MonitorUpdateResponse.php b/src/Monitors/MonitorUpdateResponse.php index 0100ff2..fe79268 100644 --- a/src/Monitors/MonitorUpdateResponse.php +++ b/src/Monitors/MonitorUpdateResponse.php @@ -8,6 +8,9 @@ use ContextDev\Core\Attributes\Required; use ContextDev\Core\Concerns\SdkModel; use ContextDev\Core\Contracts\BaseModel; +use ContextDev\Monitors\MonitorUpdateResponse\Baseline\MonitorsExtractBaseline; +use ContextDev\Monitors\MonitorUpdateResponse\Baseline\MonitorsPageBaseline; +use ContextDev\Monitors\MonitorUpdateResponse\Baseline\MonitorsSitemapBaseline; use ContextDev\Monitors\MonitorUpdateResponse\ChangeDetection; use ContextDev\Monitors\MonitorUpdateResponse\ChangeDetection\MonitorsExactChangeDetection; use ContextDev\Monitors\MonitorUpdateResponse\ChangeDetection\MonitorsSemanticChangeDetection; @@ -26,9 +29,11 @@ * * @phpstan-import-type ChangeDetectionVariants from \ContextDev\Monitors\MonitorUpdateResponse\ChangeDetection * @phpstan-import-type TargetVariants from \ContextDev\Monitors\MonitorUpdateResponse\Target + * @phpstan-import-type BaselineVariants from \ContextDev\Monitors\MonitorUpdateResponse\Baseline * @phpstan-import-type ChangeDetectionShape from \ContextDev\Monitors\MonitorUpdateResponse\ChangeDetection * @phpstan-import-type ScheduleShape from \ContextDev\Monitors\MonitorUpdateResponse\Schedule * @phpstan-import-type TargetShape from \ContextDev\Monitors\MonitorUpdateResponse\Target + * @phpstan-import-type BaselineShape from \ContextDev\Monitors\MonitorUpdateResponse\Baseline * @phpstan-import-type LastErrorShape from \ContextDev\Monitors\MonitorUpdateResponse\LastError * @phpstan-import-type WebhookShape from \ContextDev\Monitors\MonitorUpdateResponse\Webhook * @@ -42,6 +47,7 @@ * status: Status|value-of, * target: TargetShape, * updatedAt: \DateTimeInterface, + * baseline?: BaselineShape|null, * lastChangeAt?: \DateTimeInterface|null, * lastError?: null|LastError|LastErrorShape, * lastRunAt?: \DateTimeInterface|null, @@ -105,6 +111,14 @@ final class MonitorUpdateResponse implements BaseModel #[Required('updated_at')] public \DateTimeInterface $updatedAt; + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @var BaselineVariants|null $baseline + */ + #[Optional(nullable: true)] + public MonitorsPageBaseline|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline; + #[Optional('last_change_at', nullable: true)] public ?\DateTimeInterface $lastChangeAt; @@ -182,6 +196,7 @@ public function __construct() * @param Schedule|ScheduleShape $schedule * @param Status|value-of $status * @param TargetShape $target + * @param BaselineShape|null $baseline * @param LastError|LastErrorShape|null $lastError * @param list|null $tags * @param Webhook|WebhookShape|null $webhook @@ -196,6 +211,7 @@ public static function with( Status|string $status, MonitorsPageTarget|array|MonitorsSitemapTarget|MonitorsExtractTarget $target, \DateTimeInterface $updatedAt, + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline = null, ?\DateTimeInterface $lastChangeAt = null, LastError|array|null $lastError = null, ?\DateTimeInterface $lastRunAt = null, @@ -215,6 +231,7 @@ public static function with( $self['target'] = $target; $self['updatedAt'] = $updatedAt; + null !== $baseline && $self['baseline'] = $baseline; null !== $lastChangeAt && $self['lastChangeAt'] = $lastChangeAt; null !== $lastError && $self['lastError'] = $lastError; null !== $lastRunAt && $self['lastRunAt'] = $lastRunAt; @@ -324,6 +341,20 @@ public function withUpdatedAt(\DateTimeInterface $updatedAt): self return $self; } + /** + * Current baseline: the last observed value the monitor compares new snapshots against. Its shape follows `target.type` (page/sitemap/extract). Only populated on GET /monitors/{monitor_id}; null until the first baseline run completes (and after a target or change_detection update, which resets the baseline). + * + * @param BaselineShape|null $baseline + */ + public function withBaseline( + MonitorsPageBaseline|array|MonitorsSitemapBaseline|MonitorsExtractBaseline|null $baseline, + ): self { + $self = clone $this; + $self['baseline'] = $baseline; + + return $self; + } + public function withLastChangeAt(?\DateTimeInterface $lastChangeAt): self { $self = clone $this; diff --git a/src/Monitors/MonitorUpdateResponse/Baseline.php b/src/Monitors/MonitorUpdateResponse/Baseline.php new file mode 100644 index 0000000..4478f02 --- /dev/null +++ b/src/Monitors/MonitorUpdateResponse/Baseline.php @@ -0,0 +1,39 @@ +|array + */ + public static function variants(): array + { + return [ + MonitorsPageBaseline::class, + MonitorsSitemapBaseline::class, + MonitorsExtractBaseline::class, + ]; + } +} diff --git a/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsExtractBaseline.php b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsExtractBaseline.php new file mode 100644 index 0000000..ecc2c53 --- /dev/null +++ b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsExtractBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsExtractBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + #[Required] + public mixed $data; + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @var list $urlsAnalyzed + */ + #[Required('urls_analyzed', list: 'string')] + public array $urlsAnalyzed; + + /** + * `new MonitorsExtractBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsExtractBaseline::with(capturedAt: ..., data: ..., urlsAnalyzed: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsExtractBaseline) + * ->withCapturedAt(...) + * ->withData(...) + * ->withURLsAnalyzed(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urlsAnalyzed + */ + public static function with( + \DateTimeInterface $capturedAt, + mixed $data, + array $urlsAnalyzed + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['data'] = $data; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The extracted structured data, matching the monitor's extraction schema (same shape as the /web/extract endpoint's `data`). Refreshed when the monitor re-discovers its page set (at most about once a day); `null` when no extraction has been captured yet. + */ + public function withData(mixed $data): self + { + $self = clone $this; + $self['data'] = $data; + + return $self; + } + + /** + * The page URLs the monitor tracks and analyzes for changes. + * + * @param list $urlsAnalyzed + */ + public function withURLsAnalyzed(array $urlsAnalyzed): self + { + $self = clone $this; + $self['urlsAnalyzed'] = $urlsAnalyzed; + + return $self; + } +} diff --git a/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsPageBaseline.php b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsPageBaseline.php new file mode 100644 index 0000000..36681a1 --- /dev/null +++ b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsPageBaseline.php @@ -0,0 +1,92 @@ + */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * The page's visible text as last observed. + */ + #[Required] + public string $text; + + /** + * `new MonitorsPageBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsPageBaseline::with(capturedAt: ..., text: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsPageBaseline)->withCapturedAt(...)->withText(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + */ + public static function with( + \DateTimeInterface $capturedAt, + string $text + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['text'] = $text; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * The page's visible text as last observed. + */ + public function withText(string $text): self + { + $self = clone $this; + $self['text'] = $text; + + return $self; + } +} diff --git a/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsSitemapBaseline.php b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsSitemapBaseline.php new file mode 100644 index 0000000..5f6e865 --- /dev/null +++ b/src/Monitors/MonitorUpdateResponse/Baseline/MonitorsSitemapBaseline.php @@ -0,0 +1,120 @@ + + * } + */ +final class MonitorsSitemapBaseline implements BaseModel +{ + /** @use SdkModel */ + use SdkModel; + + /** + * When this baseline was last captured or replaced. + */ + #[Required('captured_at')] + public \DateTimeInterface $capturedAt; + + /** + * Number of URLs in the baseline. + */ + #[Required('url_count')] + public int $urlCount; + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @var list $urls + */ + #[Required(list: 'string')] + public array $urls; + + /** + * `new MonitorsSitemapBaseline()` is missing required properties by the API. + * + * To enforce required parameters use + * ``` + * MonitorsSitemapBaseline::with(capturedAt: ..., urlCount: ..., urls: ...) + * ``` + * + * Otherwise ensure the following setters are called + * + * ``` + * (new MonitorsSitemapBaseline) + * ->withCapturedAt(...) + * ->withURLCount(...) + * ->withURLs(...) + * ``` + */ + public function __construct() + { + $this->initialize(); + } + + /** + * Construct an instance from the required parameters. + * + * You must use named parameters to construct any parameters with a default value. + * + * @param list $urls + */ + public static function with( + \DateTimeInterface $capturedAt, + int $urlCount, + array $urls + ): self { + $self = new self; + + $self['capturedAt'] = $capturedAt; + $self['urlCount'] = $urlCount; + $self['urls'] = $urls; + + return $self; + } + + /** + * When this baseline was last captured or replaced. + */ + public function withCapturedAt(\DateTimeInterface $capturedAt): self + { + $self = clone $this; + $self['capturedAt'] = $capturedAt; + + return $self; + } + + /** + * Number of URLs in the baseline. + */ + public function withURLCount(int $urlCount): self + { + $self = clone $this; + $self['urlCount'] = $urlCount; + + return $self; + } + + /** + * The sitemap URLs as last observed (sorted, normalized). + * + * @param list $urls + */ + public function withURLs(array $urls): self + { + $self = clone $this; + $self['urls'] = $urls; + + return $self; + } +} diff --git a/src/Monitors/MonitorUpdateResponse/ChangeDetection/MonitorsSemanticChangeDetection.php b/src/Monitors/MonitorUpdateResponse/ChangeDetection/MonitorsSemanticChangeDetection.php index 9941171..bf2c5e8 100644 --- a/src/Monitors/MonitorUpdateResponse/ChangeDetection/MonitorsSemanticChangeDetection.php +++ b/src/Monitors/MonitorUpdateResponse/ChangeDetection/MonitorsSemanticChangeDetection.php @@ -10,10 +10,10 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Detect meaning-level changes that match a natural language query. + * Detect meaning-level changes to the extracted data, ignoring cosmetic or paraphrase-only differences. What is watched is determined by the extract target's `schema` and `instructions`. * * @phpstan-type MonitorsSemanticChangeDetectionShape = array{ - * query: string, type: 'semantic', confidenceThreshold?: float|null + * type: 'semantic', confidenceThreshold?: float|null * } */ final class MonitorsSemanticChangeDetection implements BaseModel @@ -25,26 +25,9 @@ final class MonitorsSemanticChangeDetection implements BaseModel #[Required] public string $type = 'semantic'; - #[Required] - public string $query; - #[Optional('confidence_threshold')] public ?float $confidenceThreshold; - /** - * `new MonitorsSemanticChangeDetection()` is missing required properties by the API. - * - * To enforce required parameters use - * ``` - * MonitorsSemanticChangeDetection::with(query: ...) - * ``` - * - * Otherwise ensure the following setters are called - * - * ``` - * (new MonitorsSemanticChangeDetection)->withQuery(...) - * ``` - */ public function __construct() { $this->initialize(); @@ -55,27 +38,15 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - string $query, - ?float $confidenceThreshold = null - ): self { + public static function with(?float $confidenceThreshold = null): self + { $self = new self; - $self['query'] = $query; - null !== $confidenceThreshold && $self['confidenceThreshold'] = $confidenceThreshold; return $self; } - public function withQuery(string $query): self - { - $self = clone $this; - $self['query'] = $query; - - return $self; - } - /** * @param 'semantic' $type */ diff --git a/src/Monitors/MonitorUpdateResponse/Target/MonitorsExtractTarget.php b/src/Monitors/MonitorUpdateResponse/Target/MonitorsExtractTarget.php index ce54f24..444d63d 100644 --- a/src/Monitors/MonitorUpdateResponse/Target/MonitorsExtractTarget.php +++ b/src/Monitors/MonitorUpdateResponse/Target/MonitorsExtractTarget.php @@ -10,13 +10,13 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a site's extracted structured data. + * Watch the monitor-relevant pages of a site for meaningful changes. A crawl guided by `schema`/`instructions` selects up to `max_pages` relevant pages to track; each run re-checks exactly those pages, and confirmed content changes are judged against the monitor's instructions. The tracked page set is refreshed by a periodic re-discovery crawl. * * @phpstan-type MonitorsExtractTargetShape = array{ + * instructions: string, * type: 'extract', * url: string, * followSubdomains?: bool|null, - * instructions?: string|null, * maxDepth?: int|null, * maxPages?: int|null, * schema?: array|null, @@ -31,6 +31,12 @@ final class MonitorsExtractTarget implements BaseModel #[Required] public string $type = 'extract'; + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + #[Required] + public string $instructions; + /** * Root URL to extract structured data from. */ @@ -40,12 +46,6 @@ final class MonitorsExtractTarget implements BaseModel #[Optional('follow_subdomains')] public ?bool $followSubdomains; - /** - * Optional natural-language instructions guiding what to extract. - */ - #[Optional] - public ?string $instructions; - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -53,13 +53,13 @@ final class MonitorsExtractTarget implements BaseModel public ?int $maxDepth; /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ #[Optional('max_pages')] public ?int $maxPages; /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @var array|null $schema */ @@ -71,13 +71,13 @@ final class MonitorsExtractTarget implements BaseModel * * To enforce required parameters use * ``` - * MonitorsExtractTarget::with(url: ...) + * MonitorsExtractTarget::with(instructions: ..., url: ...) * ``` * * Otherwise ensure the following setters are called * * ``` - * (new MonitorsExtractTarget)->withURL(...) + * (new MonitorsExtractTarget)->withInstructions(...)->withURL(...) * ``` */ public function __construct() @@ -93,19 +93,19 @@ public function __construct() * @param array|null $schema */ public static function with( + string $instructions, string $url, ?bool $followSubdomains = null, - ?string $instructions = null, ?int $maxDepth = null, ?int $maxPages = null, ?array $schema = null, ): self { $self = new self; + $self['instructions'] = $instructions; $self['url'] = $url; null !== $followSubdomains && $self['followSubdomains'] = $followSubdomains; - null !== $instructions && $self['instructions'] = $instructions; null !== $maxDepth && $self['maxDepth'] = $maxDepth; null !== $maxPages && $self['maxPages'] = $maxPages; null !== $schema && $self['schema'] = $schema; @@ -113,6 +113,17 @@ public static function with( return $self; } + /** + * Natural-language instructions guiding which pages and facts to track and which changes to report. + */ + public function withInstructions(string $instructions): self + { + $self = clone $this; + $self['instructions'] = $instructions; + + return $self; + } + /** * @param 'extract' $type */ @@ -143,17 +154,6 @@ public function withFollowSubdomains(bool $followSubdomains): self return $self; } - /** - * Optional natural-language instructions guiding what to extract. - */ - public function withInstructions(string $instructions): self - { - $self = clone $this; - $self['instructions'] = $instructions; - - return $self; - } - /** * Optional maximum link depth from the starting URL (0 = only the starting page). */ @@ -166,7 +166,7 @@ public function withMaxDepth(int $maxDepth): self } /** - * Maximum number of pages to analyze during extraction. + * Maximum number of pages to track. */ public function withMaxPages(int $maxPages): self { @@ -177,7 +177,7 @@ public function withMaxPages(int $maxPages): self } /** - * JSON Schema describing the structured data to extract and watch for changes. If omitted, a default summary + key-points schema is used. + * JSON Schema describing the data you care about. It guides which pages are selected for tracking and gives the change judge context on what matters. If omitted, a default summary + key-points schema is used. * * @param array $schema */ diff --git a/src/Monitors/MonitorUpdateResponse/Target/MonitorsSitemapTarget.php b/src/Monitors/MonitorUpdateResponse/Target/MonitorsSitemapTarget.php index a02ae7f..28a9b2f 100644 --- a/src/Monitors/MonitorUpdateResponse/Target/MonitorsSitemapTarget.php +++ b/src/Monitors/MonitorUpdateResponse/Target/MonitorsSitemapTarget.php @@ -10,7 +10,7 @@ use ContextDev\Core\Contracts\BaseModel; /** - * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. A new URL set must be observed on two consecutive runs before a change is reported, suppressing one-run crawl flaps. + * Watch a sitemap for URL additions and removals. Crawled URLs are normalized (lowercased host, no trailing slash/fragment) and scoped to the monitored site and its subdomains before comparison. On a detected difference the sitemap is re-fetched within the same run and only URLs both observations agree on are reported, suppressing transient crawl flaps. * * @phpstan-type MonitorsSitemapTargetShape = array{ * type: 'sitemap', diff --git a/src/ServiceContracts/BrandContract.php b/src/ServiceContracts/BrandContract.php index b68e7c3..ca83c91 100644 --- a/src/ServiceContracts/BrandContract.php +++ b/src/ServiceContracts/BrandContract.php @@ -24,6 +24,7 @@ interface BrandContract * @param string $name Company name to retrieve brand data for (e.g., 'Apple Inc'). * @param string $email Email address to retrieve brand data for (e.g., 'jane@stripe.com'). * @param string $ticker Stock ticker symbol to retrieve brand data for (e.g., 'AAPL'). + * @param string $directURL Full http(s) URL to fetch brand data from (e.g., 'https://stripe.com/enterprise'). Only this URL is fetched — not the entire internet. * @param string $transactionInfo transaction information to identify the brand * @param ForceLanguage|value-of $forceLanguage * @param int $maxAgeMs Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year. @@ -45,6 +46,7 @@ public function retrieve( string $name, string $email, string $ticker, + string $directURL, string $transactionInfo, ForceLanguage|string|null $forceLanguage = null, ?int $maxAgeMs = null, diff --git a/src/ServiceContracts/MonitorsContract.php b/src/ServiceContracts/MonitorsContract.php index 516d8e3..d18f9e9 100644 --- a/src/ServiceContracts/MonitorsContract.php +++ b/src/ServiceContracts/MonitorsContract.php @@ -110,7 +110,7 @@ public function update( * * @param ChangeDetectionType|value-of $changeDetectionType * @param string $q free-text search term, matched against the fields named in `search_by` - * @param list> $searchBy Comma-separated fields to search with `q`. Defaults to all of them. Note `query` only exists on semantic monitors. + * @param list> $searchBy Comma-separated fields to search with `q`. Defaults to all of them. Note `instructions` only exists on extract monitors. * @param SearchType|value-of $searchType `prefix` for as-you-type prefix matching (default), `exact` for full-token matching * @param \ContextDev\Monitors\MonitorListParams\Status|value-of<\ContextDev\Monitors\MonitorListParams\Status> $status Monitor lifecycle status. `failed` means the most recent run failed (see the monitor's `last_error`); failed monitors keep running on schedule and flip back to `active` on the next successful run. Monitors are auto-`paused` after repeated consecutive failures or insufficient-credit skips; resume by PATCHing status to `active`. * @param string $tag filter to items that have this tag diff --git a/src/Services/BrandRawService.php b/src/Services/BrandRawService.php index c6d80b7..b0f6c26 100644 --- a/src/Services/BrandRawService.php +++ b/src/Services/BrandRawService.php @@ -31,7 +31,7 @@ public function __construct(private Client $client) {} /** * @api * - * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, or transaction descriptor. + * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, transaction descriptor, or direct URL. Note: `by_direct_url` fetches brand data only from the provided URL — not from the entire internet. * * @param array{ * domain: string, @@ -45,6 +45,7 @@ public function __construct(private Client $client) {} * email: string, * ticker: string, * tickerExchange?: string, + * directURL: string, * transactionInfo: string, * city?: string, * highConfidenceOnly?: bool, diff --git a/src/Services/BrandService.php b/src/Services/BrandService.php index 58fa3da..63e08ab 100644 --- a/src/Services/BrandService.php +++ b/src/Services/BrandService.php @@ -35,13 +35,14 @@ public function __construct(private Client $client) /** * @api * - * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, or transaction descriptor. + * Retrieve logos, backdrops, colors, industry, description, and more. Provide exactly one lookup identifier in the request body: a domain, company name, email address, stock ticker, transaction descriptor, or direct URL. Note: `by_direct_url` fetches brand data only from the provided URL — not from the entire internet. * * @param string $domain Domain name to retrieve brand data for (e.g., 'stripe.com'). * @param Type|value-of $type discriminator for transaction-based brand retrieval * @param string $name Company name to retrieve brand data for (e.g., 'Apple Inc'). * @param string $email Email address to retrieve brand data for (e.g., 'jane@stripe.com'). * @param string $ticker Stock ticker symbol to retrieve brand data for (e.g., 'AAPL'). + * @param string $directURL Full http(s) URL to fetch brand data from (e.g., 'https://stripe.com/enterprise'). Only this URL is fetched — not the entire internet. * @param string $transactionInfo transaction information to identify the brand * @param ForceLanguage|value-of $forceLanguage * @param int $maxAgeMs Maximum age in milliseconds for cached brand data before the API performs a hard refresh. Defaults to 3 months (7776000000 ms). Values below 1 day (86400000 ms) are clamped to 1 day; values above 1 year (31536000000 ms) are clamped to 1 year. @@ -63,6 +64,7 @@ public function retrieve( string $name, string $email, string $ticker, + string $directURL, string $transactionInfo, ForceLanguage|string|null $forceLanguage = null, ?int $maxAgeMs = null, @@ -89,6 +91,7 @@ public function retrieve( 'email' => $email, 'ticker' => $ticker, 'tickerExchange' => $tickerExchange, + 'directURL' => $directURL, 'transactionInfo' => $transactionInfo, 'city' => $city, 'highConfidenceOnly' => $highConfidenceOnly, diff --git a/src/Services/MonitorsService.php b/src/Services/MonitorsService.php index 571f0cb..98cfc61 100644 --- a/src/Services/MonitorsService.php +++ b/src/Services/MonitorsService.php @@ -175,7 +175,7 @@ public function update( * * @param ChangeDetectionType|value-of $changeDetectionType * @param string $q free-text search term, matched against the fields named in `search_by` - * @param list> $searchBy Comma-separated fields to search with `q`. Defaults to all of them. Note `query` only exists on semantic monitors. + * @param list> $searchBy Comma-separated fields to search with `q`. Defaults to all of them. Note `instructions` only exists on extract monitors. * @param SearchType|value-of $searchType `prefix` for as-you-type prefix matching (default), `exact` for full-token matching * @param \ContextDev\Monitors\MonitorListParams\Status|value-of<\ContextDev\Monitors\MonitorListParams\Status> $status Monitor lifecycle status. `failed` means the most recent run failed (see the monitor's `last_error`); failed monitors keep running on schedule and flip back to `active` on the next successful run. Monitors are auto-`paused` after repeated consecutive failures or insufficient-credit skips; resume by PATCHing status to `active`. * @param string $tag filter to items that have this tag diff --git a/src/Version.php b/src/Version.php index 7f441e3..e53c1a9 100644 --- a/src/Version.php +++ b/src/Version.php @@ -5,5 +5,5 @@ namespace ContextDev; // x-release-please-start-version -const VERSION = '2.0.0'; +const VERSION = '2.1.0'; // x-release-please-end diff --git a/tests/ClientTest.php b/tests/ClientTest.php index d854fd5..07473e5 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -38,6 +38,7 @@ public function testDefaultHeaders(): void name: 'xxx', email: 'dev@stainless.com', ticker: 'ticker', + directURL: 'https://example.com', transactionInfo: 'xxx', ); diff --git a/tests/Services/BrandTest.php b/tests/Services/BrandTest.php index fdb0f1e..7a91ba7 100644 --- a/tests/Services/BrandTest.php +++ b/tests/Services/BrandTest.php @@ -42,6 +42,7 @@ public function testRetrieve(): void name: 'xxx', email: 'dev@stainless.com', ticker: 'ticker', + directURL: 'https://example.com', transactionInfo: 'xxx', ); @@ -68,6 +69,7 @@ public function testRetrieveWithOptionalParams(): void email: 'dev@stainless.com', ticker: 'ticker', tickerExchange: 'ticker_exchange', + directURL: 'https://example.com', transactionInfo: 'xxx', city: 'city', highConfidenceOnly: true,