From 14d8c3631d70c931d1bf1262af3f7ffd34bf1257 Mon Sep 17 00:00:00 2001 From: Cloud IX Team Date: Mon, 15 Jun 2026 08:45:59 -0700 Subject: [PATCH] Introduce the agent-platform-alert-configuration skill PiperOrigin-RevId: 932483912 --- .../SKILL.md | 288 ++++++++++++++ .../assets/alerts_initial_duplicate.tf | 19 + .../assets/draft_invalid_query.tf | 11 + .../assets/mock_bursty.json | 1 + .../assets/mock_seasonal.json | 1 + .../assets/mock_steady.json | 1 + .../references/has_historical_traffic_data.md | 47 +++ .../references/no_historical_traffic_data.md | 69 ++++ .../references/promql_queries.md | 132 +++++++ .../scripts/analyze_traffic.py | 342 ++++++++++++++++ .../scripts/analyze_traffic_test.py | 180 +++++++++ .../scripts/create_online_monitor.py | 170 ++++++++ .../scripts/create_online_monitor_test.py | 187 +++++++++ .../scripts/validate_config.py | 370 ++++++++++++++++++ .../scripts/validate_config_test.py | 289 ++++++++++++++ 15 files changed, 2107 insertions(+) create mode 100644 skills/cloud/agent-platform-alert-configuration/SKILL.md create mode 100644 skills/cloud/agent-platform-alert-configuration/assets/alerts_initial_duplicate.tf create mode 100644 skills/cloud/agent-platform-alert-configuration/assets/draft_invalid_query.tf create mode 100644 skills/cloud/agent-platform-alert-configuration/assets/mock_bursty.json create mode 100644 skills/cloud/agent-platform-alert-configuration/assets/mock_seasonal.json create mode 100644 skills/cloud/agent-platform-alert-configuration/assets/mock_steady.json create mode 100644 skills/cloud/agent-platform-alert-configuration/references/has_historical_traffic_data.md create mode 100644 skills/cloud/agent-platform-alert-configuration/references/no_historical_traffic_data.md create mode 100644 skills/cloud/agent-platform-alert-configuration/references/promql_queries.md create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic.py create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic_test.py create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor.py create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor_test.py create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/validate_config.py create mode 100644 skills/cloud/agent-platform-alert-configuration/scripts/validate_config_test.py diff --git a/skills/cloud/agent-platform-alert-configuration/SKILL.md b/skills/cloud/agent-platform-alert-configuration/SKILL.md new file mode 100644 index 0000000000..b7fc5fa547 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/SKILL.md @@ -0,0 +1,288 @@ +--- +name: agent-platform-alert-configuration +description: >- + Configures best-practice alerting policies for Google Cloud Vertex AI / Agent + Platform agents on Agent Runtime. Use when analyzing, writing, or deploying + alerting policies to monitor agent latency, error rates, and quality metrics + (response quality, tool use, hallucination). Also use when provisioning online + monitors for quality evaluation, or analyzing live metrics traffic footprints. + NOTE: This skill currently only works for the Agent Runtime. Don't use for + configuring general GCP Cloud Monitoring alerts, standard GCP alert policies, + or non-agent GCP alerting policies. +--- + +# Agent Platform Alert Configuration + +This skill provides dynamic threshold alerting configurations for Google Cloud / +Vertex AI Reasoning Engines (Agent Platform container deployments) using +extended 1-week lookback retention baselines. Standard static thresholds (e.g., +"latency > 2s") cause excessive alert noise for AI agents. Dynamic PromQL +baselines solve this. + +-------------------------------------------------------------------------------- + +## CRITICAL RULES + +* **Always configure both Reliability and Quality alerting policies** for the + target agent (6 policies in total): + * **For Reliability Monitoring**: You MUST configure exactly three + alerting policies: + 1. **Latency** (anomaly monitoring) + 2. **Error Rate - Fast Burn SLO** (1-Hour Window) + 3. **Error Rate - Slow Burn SLO** (3-Day Window) + * **For Quality Monitoring**: You MUST configure exactly three alerting + policies: + 1. **Final Response Quality** + 2. **Tool Use Quality** + 3. **Hallucination** +* **Always Provision Online Monitor**: Because quality alerting policies rely + on metrics exported by Online Monitors, you MUST ensure the Online Monitor + is provisioned for the agent: + 1. Install the required dependencies first by running: `pip install + google-cloud-monitoring google-cloud-aiplatform google-auth` + 2. Execute the provisioning script: `python3 + scripts/create_online_monitor.py ...` (see the Programmatic Online + Monitor Creation section below). + 3. If execution fails (e.g. due to missing libraries or environment sandbox + restrictions), you MUST provide the exact concrete python execution + command with all parameter values (such as project ID and agent resource + name) fully populated in your final response as a concrete plan for the + user to run. Do not merely state that the user should "ensure it is + active". +* **PromQL for Reliability (No MQL or Threshold Filters)**: For the 3 + reliability metrics, you MUST use `condition_prometheus_query_language` with + PromQL. Do **NOT** use MQL or standard `condition_threshold`. +* **Standard Threshold Filters for Agent Quality**: For the 3 agent quality + metrics, you MUST use standard `condition_threshold` filters matching the + monitored resource type `aiplatform.googleapis.com/OnlineEvaluator` and + metric type `aiplatform.googleapis.com/online_evaluator/scores`. Do **NOT** + use PromQL. +* **Install Terraform if Necessary**: You should use terraform to deploy and + must install terraform if you can't find a valid install. +* **Terraform Only**: Write the generated observability configuration ONLY as + Terraform (`.tf`) files (e.g., `alerts.tf`, `variables.tf`). +* **Dynamic Multi-Resource Alerting (No Single-Resource Pinning)**: You MUST + NOT hardcode specific agent IDs or resource name filters (e.g., + `{reasoning_engine_id="[AGENT_ID]"}` or + `metric.labels.agent_resource_name="[AGENT_NAME]"`) in alerting conditions + unless explicitly requested. Alerting policies must be written to cover all + active agents in the project dynamically: + * **For Reliability Metrics using PromQL**: ALWAYS use grouping + aggregations (`by (reasoning_engine_id)`) instead of filtering to a + single ID. This allows a single alert policy to dynamically track each + reasoning engine instance separately. + * **For Quality Metrics using Standard Threshold Filters**: Omit the + `agent_resource_name` filter entirely. Configure the condition filter to + only target the monitored resource type + (`aiplatform.googleapis.com/OnlineEvaluator`) and metric type + (`aiplatform.googleapis.com/online_evaluator/scores`) globally for the + project. +* **Check for Pre-existing Policies**: Avoid creating duplicate alert policies + for a reasoning engine: scan the target directory or workspace to see if a + policy already exists that targets the same metrics using aggregations + grouped by `reasoning_engine_id`. +* **Metric Scope Discovery & Project Inference**: Centralize alert policies in + a Metric Scope (scoping project) to save costs. Identify if a scope is used + and where policies should live by checking: + 1. **GCP CLI Check**: Run `gcloud beta monitoring metrics-scopes list + projects/[PROJECT_ID]`. If a parent scope + `locations/global/metricsScopes/[SCOPING_PROJECT_ID]` is returned, a + Metric Scope is active; deploy policies there. + 2. **Infrastructure as Code Scan**: Search Terraform configurations for + `google_monitoring_monitored_project` resources and extract the scoping + project from the `metrics_scope` attribute. + 3. **Ambiguity Fallback**: If unable to determine, ask the user: "Are you + using a multi-project Cloud Monitoring Metric Scope? If so, what is the + scoping project ID?" Deploy policies to the deduced scoping project + (setting the `project` attribute in HCL), or default to the local + project. +* **Directory Inference**: Deploy configuration files to target Terraform or + SRE folders (e.g. `monitoring/`, `ops/`, `sre/`). Use tools to locate where + alert policies or state pointers exist in the project, rather than blindly + writing to the current working directory. +* **Notification Channels**: By default, never configure any notification + channels without user input. If the user explicitly provides a notification + channel in their prompt, configure the alerts to use it. If no notification + channel is provided, you must prompt the user in your response to ask if + they would like to configure one. **IMPORTANT** Do NOT make assumptions + about notification channels. If you search the codebase for a notification + channel you must ALWAYS confirm with the user before using it. +* **Plain English Response**: You MUST include a plain English explanation for + what the alerts do in your response. This must explain in plain English what + the alert measures, how the algorithm works, and what a trigger indicates. + +-------------------------------------------------------------------------------- + +## Algorithm Selection & Policy Mapping Process + +Alerting policies for reasoning engine agents MUST map to the correct algorithms +to ensure statistical stability and prevent alert noise or blind spots based on +data classes: + +* **Latency**: Follows workload traffic pattern (Steady -> Z-Score; Seasonal + -> Seasonal Decomposition; Bursty -> Moving Averages). +* **Error Rate**: ALWAYS use **Multi-Window Multi-Burn Rate SLOs** (or + ratio-based static thresholds). Error rate is naturally sparse (normally + `0`). When standard deviation is `0`, Z-score computation is mathematically + unstable (division-by-zero or NaN), causing false alert storms. + +To resolve the workload traffic pattern (Seasonal, Steady, or Bursty), follow +the instructions corresponding to the availability of historical metrics data: + +* **Case 1: No historical metrics data available (e.g., brand new agent)**: + You MUST read and follow: + [no_historical_traffic_data.md](references/no_historical_traffic_data.md) +* **Case 2: Historical metrics data available (e.g., active agent with + traffic)**: You MUST read and follow: + [has_historical_traffic_data.md](references/has_historical_traffic_data.md) + +-------------------------------------------------------------------------------- + +## Telemetry Metrics and PromQL Examples + +All raw telemetry metrics for the Agent Platform are cumulative **counters**. +Because we monitor their rates or quantiles, we can optimize the PromQL queries +by using longer range windows (e.g., `[1w]`) for historical averages instead of +expensive `avg_over_time` subqueries. + +Signal | Raw Metric | Type | Description +:------------- | :------------------------------------------ | :------ | :---------- +**Latency** | `reasoning_engine_request_latencies_bucket` | Counter | Histogram bucket of request latencies +**Error Rate** | `reasoning_engine_request_count` | Counter | Cumulative count of requests + +-------------------------------------------------------------------------------- + +For the specific PromQL queries corresponding to each algorithm, you MUST read +and follow: [promql_queries.md](references/promql_queries.md) + +-------------------------------------------------------------------------------- + +## Agent Quality Metrics (Online Monitor) + +All agent quality evaluation metrics are exported by Online Monitors to the +monitored resource type `aiplatform.googleapis.com/OnlineEvaluator` under the +metric type `aiplatform.googleapis.com/online_evaluator/scores`. + +Because the scores metric is of value type `DISTRIBUTION`, standard mean-based +PromQL or arithmetic `ALIGN_MEAN` aligners are unsupported. You MUST use a +percentile aligner (typically `ALIGN_PERCENTILE_50` to evaluate the median +score) within the `aggregations` block of your `condition_threshold`. + +Signal | Metric Name (`evaluation_metric_name`) | Target Threshold | Recommended Aligner +:------------------------------- | :------------------------------------- | :------------------ | :------------------ +**Final Response Quality** | `final_response_quality_v1` | `< 0.8` (or custom) | `ALIGN_PERCENTILE_50` +**Tool Use Quality** | `tool_use_quality_v1` | `< 0.8` (or custom) | `ALIGN_PERCENTILE_50` +**Hallucination (Groundedness)** | `hallucination_v1` | `< 0.9` (or custom) | `ALIGN_PERCENTILE_50` + +#### Metric Filter Example + +When configuring a quality alert policy in Terraform, use the following filter +expression structure: + +```filter +resource.type="aiplatform.googleapis.com/OnlineEvaluator" +AND metric.type="aiplatform.googleapis.com/online_evaluator/scores" +AND metric.labels.evaluation_metric_name="[METRIC_NAME]" +``` + +#### Programmatic Online Monitor Creation (Python SDK) + +Because Online Monitors cannot be configured via Terraform, run +create_online_monitor.py with the command below to generate required metrics for +the alerting policies to consume: + +* **Command**: `python3 scripts/create_online_monitor.py --project-id + "[PROJECT_ID]" --agent-resource-name "[AGENT_RESOURCE_NAME]" + [--sampling-percentage [PERCENTAGE]]` +* **Sampling Rate Recommendation**: For production agents, configure a + conservative sampling percentage (default: **10%**) to control LLM + evaluation costs relative to your production traffic volume. For details, + refer to + [Continuous evaluation with online monitors](https://docs.cloud.google.com/gemini-enterprise-agent-platform/optimize/evaluation/evaluate-online). + +-------------------------------------------------------------------------------- + +## Prerequisites & Dependencies + +To execute the scripts included in this skill, you must install the required +Google Cloud SDK libraries. Run the following command in your terminal before +running any scripts: + +```bash +pip install google-cloud-monitoring google-cloud-aiplatform google-auth +``` + +-------------------------------------------------------------------------------- + +## Tooling Scripts + +Use the following scripts to resolve duplicates and validate configs before +presenting or applying Terraform changes: + +1. **Duplicate Check & Merge**: Checks for pre-existing alerts in the target + folder to ensure changes are merged in-place rather than appended: + * Command: `python3 scripts/validate_config.py --directory [TARGET_TF_DIR] + --engine-var "${var.reasoning_engine_id}"` +2. **Config Linting**: Validates PromQL grammar, matching engine labels, and + HCL structure: + * Command: `python3 scripts/validate_config.py --file [PATH_TO_TF_FILE]` + +-------------------------------------------------------------------------------- + +## Gotchas & Behavioral Corrections + +* **Duration Buffers (Transient Glitches)**: To avoid alerts firing on + transient spikes, use duration/retest window buffers appropriately: + * **Reliability Metrics (PromQL / Cloud Monitoring)**: + * For short-lookback alerts querying data under 25 hours (e.g., + Short-Window Z-Score, Moving Averages, Fast Burn SLO), ALWAYS use a + `duration = "300s"` (5 minutes) buffer to filter out transient cold + start/deployment spikes. + * For long-lookback alerts querying data longer than 25 hours (e.g., + Long-Window Z-Score, Seasonal Decomposition, Slow Burn SLO), + duration/retest windows are disabled by the platform. You must **not + set a duration** (omit it entirely). + * **Quality Metrics (Standard Filters / Online Monitor)**: + * Always use a `duration = "300s"` (5 minutes) buffer to filter out + transient scoring dips or evaluation outliers caused by temporary + LLM judge congestion, or edge-case query outliers. +* **Dynamic Baseline Adaptation Blind Spot**: Explain to users that dynamic + statistical Z-score thresholds compare current rates to a moving statistical + baseline. If a system degrades slowly over days, the standard baseline curve + adapts to this slow drift, making standard Z-score alerts blind to + persistent slow errors. Recommend a hard static threshold alert in parallel + for strict SLA enforcement. +* **Seasonal Decomposition Double Alerting**: The agent MUST ONLY configure + seasonal decomposition alert policies to track spikes (e.g., latency spikes) + OR drops AND MUST NOT use dual-direction checks (like absolute deviation). + Explain this limitation to the user: comparing to a historical offset (e.g., + `offset 1w`) the alert policy triggers twice if tracking both directions + (once for the anomaly, and once 1 week later when the anomaly becomes the + baseline). To prevent this, the generated policy MUST only track either + spikes (using `>`) or drops (using `<`), avoiding using `abs()`. +* **Raw Error Boundaries**: Explain that raw error counts or absolute failed + request count boundaries do not scale under changing traffic throughput. + Recommend ratio-based error rate alerts instead. +* **Safe Threshold Modulation E2E Validation**: When verifying a dynamic + metric threshold policy end-to-end, do NOT attempt to force real platform + errors. Instead, deploy the alert policy with standard safe bounds (Z-score + multiplier > 15), then temporarily update standard deviation Z-score limits + to a negative value (e.g. > -3) to trigger/verify the "Firing" state before + reverting. Always get confirmation before taking this action proactively. +* **Expected Script Failures**: + * `validate_config.py --directory` exiting with code 1: Parse the JSON + output for duplicate resource targets. Perform in-place upgrade edits, + then re-check until it passes with 0. +* **Distribution Metric Aligner Constraint**: Standard `ALIGN_MEAN` cannot be + applied to `DELTA` distribution metrics like `online_evaluator/scores`. You + MUST use percentile-based aligners (like `ALIGN_PERCENTILE_50`) to reduce + the score distribution into a comparable numeric stream. + +-------------------------------------------------------------------------------- + +## Supporting Links + +* [Continuous evaluation with online monitors](https://docs.cloud.google.com/gemini-enterprise-agent-platform/optimize/evaluation/evaluate-online) +* [Agent Platform Quality Metrics](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/rubric-metric-details) +* [Google Cloud Alerting Policies Guide](https://cloud.google.com/monitoring/alerts) +* [Google Cloud Monitoring PromQL Documentation](https://cloud.google.com/monitoring/promql) diff --git a/skills/cloud/agent-platform-alert-configuration/assets/alerts_initial_duplicate.tf b/skills/cloud/agent-platform-alert-configuration/assets/alerts_initial_duplicate.tf new file mode 100644 index 0000000000..c016f16383 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/assets/alerts_initial_duplicate.tf @@ -0,0 +1,19 @@ +resource "google_monitoring_alert_policy" "agent_error_rate_fast_burn" { + project = var.project_id + display_name = "Agent Error Rate Fast Burn" + combiner = "OR" + conditions { + display_name = "Error Rate Fast Burn" + condition_prometheus_query_language { + query = <<-EOT + ( + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2..", reasoning_engine_id="12345"}[5m])) + / + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id="12345"}[5m])) + > (1 - var.slo_target) * 3 + ) + EOT + duration = "300s" + } + } +} diff --git a/skills/cloud/agent-platform-alert-configuration/assets/draft_invalid_query.tf b/skills/cloud/agent-platform-alert-configuration/assets/draft_invalid_query.tf new file mode 100644 index 0000000000..9a7c795f53 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/assets/draft_invalid_query.tf @@ -0,0 +1,11 @@ +resource "google_monitoring_alert_policy" "draft_policy" { + project = var.project_id + display_name = "Draft Policy" + combiner = "OR" + conditions { + display_name = "Draft Condition" + condition_prometheus_query_language { + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5y])) / sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m]" + } + } +} diff --git a/skills/cloud/agent-platform-alert-configuration/assets/mock_bursty.json b/skills/cloud/agent-platform-alert-configuration/assets/mock_bursty.json new file mode 100644 index 0000000000..dec77aae96 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/assets/mock_bursty.json @@ -0,0 +1 @@ +[495.0469, 509.6423, 492.0803, 493.7152, 496.8687, 497.3225, 0.0292, 0.0601, 0.0767, 0.0376, 0.0635, 0.033, 0.0492, 0.0417, 0.0519, 0.0552, 0.0371, 0.0484, 0.0574, 0.0407, 0.0624, 0.0628, 0.0453, 0.0642, 0.0306, 0.0439, 0.0554, 0.0595, 0.061, 0.0598, 0.0504, 0.0569, 0.0503, 0.0353, 0.059, 0.0557, 0.0487, 0.0442, 0.0695, 0.0407, 0.0507, 0.043, 0.0495, 0.0614, 0.0541, 0.0469, 0.0581, 0.0593, 0.0493, 0.0457, 0.0427, 0.0311, 0.0655, 0.0429, 0.0384, 0.0415, 0.0386, 0.0611, 0.0491, 0.0402, 0.0419, 0.04, 0.0612, 0.0357, 0.0508, 0.0549, 0.0537, 0.0523, 0.0486, 0.0265, 0.0498, 0.045, 486.7916, 514.4725, 512.5739, 496.267, 503.5742, 500.0377, 0.0311, 0.0457, 0.0587, 0.0489, 0.04, 0.0613, 0.0535, 0.0384, 0.0443, 0.0458, 0.0402, 0.0576, 0.0446, 0.0514, 0.0678, 0.0433, 0.039, 0.05, 0.0455, 0.0467, 0.0409, 0.0345, 0.0461, 0.0371, 0.039, 0.0784, 0.0742, 0.0738, 0.0469, 0.039, 0.0555, 0.0397, 0.0472, 0.0407, 0.0589, 0.0489, 0.0532, 0.0439, 0.048, 0.0319, 0.049, 0.0469, 0.0542, 0.0453, 0.0401, 0.0508, 0.0646, 0.0486, 0.0678, 0.025, 0.0413, 0.0473, 0.0367, 0.0226, 0.0505, 0.0401, 0.042, 0.0486, 0.0478, 0.0438, 0.0562, 0.0421, 0.0614, 0.0622, 0.0342, 0.0412, 509.228, 487.2668, 494.2323, 501.5766, 510.0171, 511.7377, 0.0486, 0.0554, 0.0533, 0.0663, 0.0499, 0.0487, 0.048, 0.0371, 0.037, 0.0231, 0.0647, 0.0696, 0.0328, 0.0585, 0.054, 0.048, 0.0562, 0.049, 0.0765, 0.0591, 0.0421, 0.0391, 0.0638, 0.0473, 0.0478, 0.0515, 0.0507, 0.0616, 0.0406, 0.027, 0.0444, 0.0421, 0.0436, 0.0334, 0.0406, 0.0354, 0.0596, 0.0391, 0.0477, 0.0644, 0.0466, 0.0537, 0.0601, 0.0558, 0.0442, 0.0396, 0.0629, 0.0519, 0.0428, 0.0465, 0.0495, 0.0322, 0.0548, 0.0604, 0.0434, 0.0416, 0.0459, 0.048, 0.0702, 0.0585, 0.0518, 0.0439, 0.0508, 0.0482, 0.0384, 0.0457, 504.4112, 489.2926, 486.8359, 498.4292, 496.0102, 482.7873, 0.0619, 0.0618, 0.0709, 0.0454, 0.0557, 0.0623, 0.0554, 0.0615, 0.0516, 0.0451, 0.0547, 0.0549, 0.0568, 0.0609, 0.0516, 0.0479, 0.0618, 0.0485, 0.0418, 0.0595, 0.0287, 0.0612, 0.0585, 0.05, 0.0614, 0.046, 0.053, 0.0356, 0.0377, 0.0545, 0.0459, 0.0386, 0.0593, 0.0492, 0.0433, 0.0484, 0.065, 0.0608, 0.0505, 0.0564, 0.0442, 0.068, 0.0631, 0.0464, 0.052, 0.0592, 0.0596, 0.0601, 0.0569, 0.0535, 0.0341, 0.0646, 0.0466, 0.0524, 0.0497, 0.0605, 0.0458, 0.0384, 0.0647, 0.057, 0.0654, 0.0495, 0.0534, 0.059, 0.0398, 0.0345, 491.7545, 493.6087, 517.8153, 525.6872, 497.905, 500.9281, 0.0468, 0.0476, 0.0534, 0.0534, 0.0465, 0.0489, 0.044, 0.0427, 0.046, 0.0569, 0.0549, 0.0448, 0.0597, 0.0506, 0.0596, 0.0481, 0.068, 0.064, 0.0573, 0.0644, 0.0435, 0.0674, 0.0378, 0.0508, 0.0439, 0.0309, 0.037, 0.0456, 0.0549, 0.0463, 0.0594, 0.0467, 0.0407, 0.0636, 0.0621, 0.0475, 0.0339, 0.0391, 0.0448, 0.0508, 0.062, 0.0507, 0.0529, 0.0543, 0.0382, 0.0613, 0.0652, 0.0552, 0.0655, 0.0475, 0.0501, 0.0637, 0.0483, 0.0439, 0.0678, 0.0485, 0.042, 0.0499, 0.0286, 0.0605, 0.0635, 0.0376, 0.0433, 0.0686, 0.0597, 0.0467, 498.3306, 504.0133, 514.2054, 507.7485, 505.6508, 497.9603, 0.0654, 0.0558, 0.039, 0.0461, 0.068, 0.0318, 0.0394, 0.0555, 0.0486, 0.0387, 0.015, 0.0467, 0.0618, 0.04, 0.0385, 0.0577, 0.064, 0.0412, 0.0465, 0.0348, 0.05, 0.0425, 0.0483, 0.0255, 0.0592, 0.0315, 0.0365, 0.0476, 0.0298, 0.0339, 0.0406, 0.0316, 0.0429, 0.0489, 0.067, 0.0587, 0.0254, 0.0479, 0.0414, 0.0546, 0.0466, 0.0535, 0.0565, 0.0461, 0.0335, 0.0459, 0.0562, 0.0557, 0.0481, 0.0567, 0.049, 0.0371, 0.0517, 0.0583, 0.0631, 0.0441, 0.0512, 0.0476, 0.0365, 0.0532, 0.0406, 0.0507, 0.0433, 0.0441, 0.0494, 0.0508, 504.9506, 495.9148, 497.1774, 503.661, 495.7969, 500.2102, 0.0442, 0.0495, 0.0582, 0.0581, 0.0582, 0.0621, 0.0515, 0.0599, 0.0468, 0.0601, 0.0576, 0.0666, 0.0583, 0.0568, 0.0532, 0.045, 0.0435, 0.0585, 0.0372, 0.0656, 0.0521, 0.0561, 0.0597, 0.0525, 0.0492, 0.0285, 0.0536, 0.0556, 0.0353, 0.0519, 0.049, 0.0604, 0.0329, 0.0444, 0.048, 0.0414, 0.0519, 0.0294, 0.0273, 0.0497, 0.0555, 0.0417, 0.0537, 0.053, 0.0558, 0.0508, 0.0329, 0.05, 0.0513, 0.065, 0.035, 0.0729, 0.0492, 0.0394, 0.0472, 0.0612, 0.0505, 0.0425, 0.0569, 0.0598, 0.0501, 0.0671, 0.0711, 0.0258, 0.0503, 0.0685, 504.9866, 514.8419, 505.7171, 485.5475, 493.6777, 499.9073, 0.0634, 0.0494, 0.0452, 0.0446, 0.0519, 0.0521, 0.073, 0.0486, 0.0376, 0.0562, 0.0527, 0.0517, 0.0429, 0.0547, 0.0451, 0.0446, 0.0441, 0.0598, 0.0357, 0.0491, 0.0474, 0.0575, 0.0482, 0.0591, 0.0424, 0.0543, 0.0528, 0.0509, 0.0656, 0.0553, 0.0527, 0.061, 0.0539, 0.0475, 0.0597, 0.0723, 0.0444, 0.0564, 0.0514, 0.0509, 0.0311, 0.0436, 0.042, 0.0643, 0.0388, 0.0633, 0.0405, 0.0483, 0.046, 0.0444, 0.0344, 0.0639, 0.0612, 0.062, 0.0597, 0.0591, 0.0624, 0.0424, 0.0557, 0.0645, 0.0535, 0.0482, 0.0412, 0.0474, 0.0453, 0.0201, 504.7448, 515.2522, 506.2786, 501.7734, 492.7512, 497.6197, 0.064, 0.0363, 0.0727, 0.035, 0.0586, 0.0433, 0.0455, 0.0285, 0.0595, 0.0543, 0.0608, 0.045, 0.0634, 0.0519, 0.0483, 0.045, 0.0624, 0.0536, 0.0542, 0.0352, 0.0494, 0.04, 0.0419, 0.0315, 0.0559, 0.0489, 0.05, 0.0402, 0.0519, 0.0401, 0.0491, 0.0563, 0.0538, 0.0559, 0.0432, 0.0306, 0.0528, 0.0393, 0.0468, 0.0382, 0.0477, 0.0567, 0.0467, 0.0378, 0.0515, 0.0527, 0.0502, 0.0528, 0.0624, 0.0666, 0.0571, 0.0465, 0.0536, 0.0361, 0.0501, 0.0686, 0.0495, 0.0499, 0.0422, 0.0612, 0.0472, 0.0531, 0.0465, 0.0379, 0.0482, 0.0399, 493.2261, 494.2891, 517.2198, 506.4776, 503.1522, 493.7094, 0.0561, 0.0486, 0.0622, 0.0478, 0.0672, 0.0477, 0.0509, 0.0453, 0.0497, 0.0402, 0.0385, 0.0334, 0.0416, 0.0405, 0.0529, 0.0436, 0.0484, 0.0631, 0.0559, 0.0344, 0.0541, 0.056, 0.0486, 0.0405, 0.0536, 0.0474, 0.0527, 0.0558, 0.0542, 0.0451, 0.0339, 0.051, 0.0488, 0.0484, 0.0533, 0.0553, 0.0392, 0.0584, 0.0535, 0.0516, 0.0507, 0.0719, 0.0545, 0.0491, 0.0341, 0.0473, 0.0548, 0.0373, 0.0485, 0.0514, 0.0579, 0.0349, 0.0482, 0.0476, 0.05, 0.0542, 0.0588, 0.061, 0.076, 0.0522, 0.0546, 0.0488, 0.0492, 0.0409, 0.0482, 0.0435, 487.2326, 501.5782, 494.3931, 502.0454, 497.1529, 505.0461, 0.0644, 0.0553, 0.0464, 0.0463, 0.0521, 0.0458, 0.0465, 0.0615, 0.0529, 0.0453, 0.0519, 0.0488, 0.0449, 0.0525, 0.046, 0.0441, 0.0571, 0.0432, 0.0639, 0.0376, 0.0546, 0.054, 0.0536, 0.053, 0.059, 0.0474, 0.0462, 0.0467, 0.0699, 0.0396, 0.0608, 0.0417, 0.0554, 0.0611, 0.0378, 0.0412, 0.0587, 0.062, 0.0441, 0.0675, 0.0473, 0.0538, 0.0627, 0.0426, 0.0666, 0.0425, 0.049, 0.0461, 0.0733, 0.0503, 0.0587, 0.0617, 0.0457, 0.059, 0.0469, 0.0327, 0.0463, 0.0505, 0.0276, 0.0381, 0.0492, 0.0543, 0.0563, 0.0498, 0.0434, 0.0558, 486.3505, 507.0814, 484.7717, 509.1429, 490.9865, 515.582, 0.0457, 0.0438, 0.0398, 0.0557, 0.0379, 0.0474, 0.0543, 0.0411, 0.0409, 0.0449, 0.0364, 0.0428, 0.0498, 0.0344, 0.0501, 0.0508, 0.0519, 0.0499, 0.0265, 0.041, 0.0531, 0.0483, 0.0386, 0.0658, 0.0624, 0.0363, 0.0485, 0.0554, 0.0662, 0.0287, 0.0564, 0.0461, 0.0516, 0.047, 0.0666, 0.0695, 0.0472, 0.0407, 0.0755, 0.0543, 0.0392, 0.0612, 0.048, 0.0467, 0.0606, 0.0328, 0.0594, 0.0518, 0.0535, 0.0519, 0.0334, 0.0423, 0.0537, 0.0551, 0.0493, 0.0334, 0.0473, 0.042, 0.0423, 0.0548, 0.053, 0.0349, 0.0487, 0.0557, 0.0519, 0.0483, 500.0842, 488.1702, 518.8654, 501.2902, 491.2894, 481.5181, 0.0323, 0.0437, 0.0603, 0.0443, 0.0473, 0.0398, 0.0649, 0.0724, 0.0226, 0.0463, 0.0549, 0.0549, 0.0448, 0.0452, 0.0567, 0.0651, 0.0321, 0.043, 0.0546, 0.0397, 0.0445, 0.0632, 0.0528, 0.0447, 0.0547, 0.0364, 0.0516, 0.0545, 0.062, 0.0564, 0.0514, 0.048, 0.0475, 0.0622, 0.0624, 0.0554, 0.0504, 0.0414, 0.0365, 0.0454, 0.0578, 0.0516, 0.0628, 0.0481, 0.0337, 0.0431, 0.0583, 0.0525, 0.0512, 0.0495, 0.0449, 0.0564, 0.0447, 0.0539, 0.0502, 0.0339, 0.0654, 0.0475, 0.0584, 0.0559, 0.0498, 0.0454, 0.052, 0.0447, 0.0416, 0.0449, 483.2593, 489.9058, 498.5701, 505.5517, 479.7498, 504.7801, 0.0633, 0.0588, 0.0636, 0.0379, 0.0403, 0.0359, 0.0637, 0.0718, 0.0339, 0.0483, 0.0366, 0.0312, 0.0405, 0.0647, 0.045, 0.058, 0.0566, 0.0416, 0.0633, 0.0391, 0.0448, 0.0544, 0.047, 0.0475, 0.0581, 0.0615, 0.0319, 0.0503, 0.0626, 0.0658, 0.0248, 0.0423, 0.0508, 0.0458, 0.0572, 0.0548, 0.0554, 0.0476, 0.0482, 0.0593, 0.0553, 0.0533, 0.0511, 0.0423, 0.0424, 0.0513, 0.0604, 0.0399, 0.0533, 0.054, 0.0474, 0.0488, 0.0564, 0.0422, 0.0639, 0.0325, 0.0566, 0.0569, 0.0638, 0.0296, 0.0394, 0.0608, 0.0422, 0.0497, 0.0607, 0.0393, 489.5629, 490.1229, 499.5785, 492.5179, 500.5466, 498.8615, 0.055, 0.0434, 0.0555, 0.0585, 0.0589, 0.0544, 0.0457, 0.051, 0.036, 0.0531, 0.0591, 0.0434, 0.0639, 0.0583, 0.0696, 0.0388, 0.0508, 0.0434, 0.0459, 0.0577, 0.0615, 0.0497, 0.0466, 0.0391, 0.0495, 0.0527, 0.0563, 0.0593, 0.0444, 0.0432, 0.0633, 0.0527, 0.0329, 0.066, 0.0432, 0.0581, 0.0467, 0.0506, 0.0667, 0.0318, 0.0468, 0.0427, 0.0473, 0.0469, 0.0592, 0.0543, 0.0453, 0.0651, 0.0584, 0.0412, 0.0463, 0.0432, 0.0521, 0.0461, 0.0575, 0.0447, 0.0501, 0.044, 0.0439, 0.0585, 0.0349, 0.0523, 0.0582, 0.0631, 0.0406, 0.0622, 498.9163, 500.9065, 502.7819, 486.8057, 513.1098, 506.9739, 0.0434, 0.0441, 0.0736, 0.048, 0.0493, 0.0337, 0.0477, 0.0552, 0.0445, 0.0515, 0.0532, 0.0463, 0.06, 0.0481, 0.0491, 0.0499, 0.032, 0.0558, 0.0577, 0.0578, 0.0544, 0.0491, 0.0554, 0.0773, 0.039, 0.056, 0.0549, 0.0584, 0.0357, 0.0701, 0.0629, 0.0477, 0.0683, 0.0517, 0.0712, 0.0522, 0.0299, 0.0497, 0.0455, 0.0363, 0.0401, 0.0528, 0.0425, 0.06, 0.0332, 0.0475, 0.0547, 0.0541, 0.0444, 0.0527, 0.0396, 0.043, 0.0358, 0.0631, 0.046, 0.0401, 0.0471, 0.0636, 0.0676, 0.0411, 0.0681, 0.0495, 0.0615, 0.0588, 0.0423, 0.0431, 483.3116, 500.377, 504.1124, 497.9767, 487.6112, 504.5393, 0.0581, 0.0517, 0.0438, 0.0534, 0.0406, 0.0528, 0.0538, 0.0606, 0.0686, 0.0685, 0.0589, 0.0447, 0.0567, 0.0359, 0.0499, 0.0488, 0.045, 0.055, 0.0457, 0.0701, 0.0483, 0.069, 0.0529, 0.0401, 0.0511, 0.0714, 0.0485, 0.0509, 0.0555, 0.058, 0.0575, 0.0331, 0.0427, 0.0447, 0.0804, 0.042, 0.0514, 0.0769, 0.0551, 0.0635, 0.0585, 0.0342, 0.0454, 0.0475, 0.032, 0.0292, 0.06, 0.0692, 0.0393, 0.0488, 0.0646, 0.0605, 0.0359, 0.0496, 0.0541, 0.0312, 0.0446, 0.0444, 0.027, 0.042, 0.0513, 0.0542, 0.037, 0.0554, 0.048, 0.0487, 494.0709, 478.2863, 508.6394, 504.6632, 499.7455, 494.7167, 0.0365, 0.0541, 0.0453, 0.0558, 0.0567, 0.045, 0.0427, 0.0483, 0.0533, 0.0328, 0.0519, 0.0354, 0.0463, 0.0731, 0.0401, 0.0398, 0.0459, 0.0478, 0.037, 0.0534, 0.0564, 0.0551, 0.0535, 0.0473, 0.0405, 0.0503, 0.0552, 0.0415, 0.0479, 0.0501, 0.0726, 0.0613, 0.0635, 0.057, 0.0439, 0.0561, 0.0652, 0.0401, 0.0506, 0.0513, 0.0556, 0.0617, 0.048, 0.0484, 0.0815, 0.0452, 0.0363, 0.0514, 0.0444, 0.0483, 0.0488, 0.0272, 0.0509, 0.0523, 0.0618, 0.0517, 0.0616, 0.0664, 0.0553, 0.0546, 0.0467, 0.0562, 0.0467, 0.0265, 0.0487, 0.0614, 508.8612, 497.3183, 492.0835, 497.3452, 499.6467, 488.3025, 0.0399, 0.0492, 0.0522, 0.0558, 0.0419, 0.0307, 0.0481, 0.0598, 0.0352, 0.0414, 0.0433, 0.0665, 0.0611, 0.0467, 0.0578, 0.043, 0.0497, 0.0316, 0.0402, 0.0405, 0.0383, 0.0556, 0.0401, 0.0601, 0.0556, 0.0524, 0.0617, 0.0525, 0.0429, 0.0457, 0.0567, 0.0372, 0.0591, 0.048, 0.0562, 0.0512, 0.0422, 0.0659, 0.0476, 0.053, 0.0447, 0.042, 0.0509, 0.045, 0.0494, 0.0513, 0.0447, 0.0598, 0.056, 0.0295, 0.0409, 0.0463, 0.0402, 0.0427, 0.0564, 0.0549, 0.0339, 0.037, 0.0525, 0.047, 0.0582, 0.0435, 0.0503, 0.0413, 0.0472, 0.056, 503.8221, 488.8878, 510.4386, 499.3604, 504.3571, 504.3077, 0.0454, 0.0587, 0.0427, 0.0462, 0.0378, 0.0345, 0.072, 0.0483, 0.0459, 0.0688, 0.0326, 0.0622, 0.0525, 0.0486, 0.0477, 0.047, 0.0649, 0.0601, 0.0487, 0.0421, 0.0502, 0.0399, 0.0419, 0.047, 0.0424, 0.0435, 0.0464, 0.0396, 0.0652, 0.0287, 0.057, 0.0567, 0.0482, 0.0484, 0.0514, 0.0504, 0.057, 0.0554, 0.0459, 0.0401, 0.0531, 0.0557, 0.0578, 0.0445, 0.0748, 0.0327, 0.0684, 0.0419, 0.0489, 0.0675, 0.0686, 0.0656, 0.058, 0.0541, 0.0361, 0.0381, 0.0492, 0.0561, 0.0572, 0.0585, 0.064, 0.0642, 0.0504, 0.0291, 0.0601, 0.0478, 495.1735, 501.6769, 487.0631, 497.7347, 485.5215, 476.7783, 0.0429, 0.0646, 0.0512, 0.0577, 0.0488, 0.054, 0.031, 0.0427, 0.0412, 0.0363, 0.0511, 0.0539, 0.0535, 0.0494, 0.057, 0.0506, 0.0527, 0.0441, 0.0554, 0.0542, 0.0397, 0.0434, 0.0606, 0.0369, 0.0458, 0.0602, 0.0567, 0.0598, 0.056, 0.0464, 0.0322, 0.0705, 0.0581, 0.0411, 0.0512, 0.0437, 0.049, 0.0559, 0.0429, 0.0689, 0.0466, 0.0455, 0.0618, 0.0384, 0.0513, 0.0712, 0.0509, 0.0522, 0.0442, 0.0407, 0.0478, 0.0728, 0.0592, 0.0554, 0.0569, 0.0596, 0.0467, 0.0498, 0.0494, 0.0545, 0.0403, 0.037, 0.0426, 0.0539, 0.039, 0.0577, 488.5348, 497.8455, 486.1783, 506.1297, 484.6796, 479.9006, 0.0506, 0.0532, 0.0577, 0.0517, 0.0542, 0.0519, 0.0521, 0.0638, 0.0479, 0.0403, 0.0631, 0.0597, 0.0557, 0.0446, 0.0548, 0.0669, 0.0453, 0.0567, 0.0366, 0.0407, 0.0598, 0.0566, 0.0465, 0.0392, 0.0426, 0.0705, 0.0506, 0.0587, 0.0558, 0.0527, 0.0545, 0.0467, 0.0613, 0.0509, 0.0468, 0.0554, 0.0497, 0.054, 0.0416, 0.0323, 0.0281, 0.0353, 0.04, 0.0442, 0.0501, 0.0548, 0.0554, 0.0383, 0.069, 0.0489, 0.044, 0.049, 0.0527, 0.0535, 0.0445, 0.0654, 0.0344, 0.0548, 0.0577, 0.0495, 0.0405, 0.0508, 0.0593, 0.0508, 0.0448, 0.0296, 503.0271, 480.5126, 510.9951, 492.238, 511.5543, 509.9011, 0.0497, 0.048, 0.0695, 0.0474, 0.047, 0.0453, 0.0425, 0.0501, 0.0485, 0.0231, 0.0395, 0.0628, 0.0586, 0.0734, 0.0333, 0.0301, 0.0608, 0.0395, 0.0588, 0.0435, 0.0618, 0.0468, 0.0486, 0.065, 0.0576, 0.0436, 0.0223, 0.0659, 0.0489, 0.0527, 0.0409, 0.0488, 0.0419, 0.0431, 0.0248, 0.0573, 0.0522, 0.042, 0.0615, 0.0478, 0.0456, 0.0397, 0.0653, 0.0711, 0.0395, 0.0396, 0.0473, 0.0578, 0.0505, 0.0483, 0.0579, 0.0521, 0.0472, 0.0482, 0.0464, 0.06, 0.0498, 0.0497, 0.0499, 0.0593, 0.0459, 0.0694, 0.0413, 0.0352, 0.0524, 0.0535, 495.7048, 532.8877, 504.3582, 512.5304, 506.1438, 503.4137, 0.0408, 0.0516, 0.0554, 0.0368, 0.0269, 0.0622, 0.0563, 0.0545, 0.0472, 0.0582, 0.0361, 0.0441, 0.0442, 0.0618, 0.0473, 0.0439, 0.0584, 0.0596, 0.0487, 0.054, 0.0371, 0.0443, 0.0407, 0.0472, 0.0576, 0.0425, 0.057, 0.0583, 0.041, 0.0716, 0.0474, 0.0391, 0.0406, 0.0546, 0.048, 0.0513, 0.0266, 0.0409, 0.0352, 0.058, 0.0436, 0.0444, 0.0506, 0.062, 0.0587, 0.059, 0.0424, 0.0467, 0.0503, 0.04, 0.0491, 0.0506, 0.0572, 0.0309, 0.0468, 0.0508, 0.0677, 0.0563, 0.0447, 0.0427, 0.0568, 0.0548, 0.0411, 0.051, 0.0652, 0.0561, 492.5012, 485.416, 506.4024, 502.2154, 510.1353, 512.9235, 0.0429, 0.0502, 0.0412, 0.0325, 0.0473, 0.0582, 0.0511, 0.0499, 0.0348, 0.0514, 0.0501, 0.0501, 0.046, 0.0469, 0.0555, 0.0539, 0.0313, 0.0531, 0.0468, 0.0512, 0.0573, 0.0571, 0.0684, 0.048, 0.0569, 0.0395, 0.0566, 0.0364, 0.0588, 0.0393, 0.037, 0.0559, 0.0574, 0.0669, 0.0466, 0.0449, 0.0493, 0.0374, 0.0418, 0.0512, 0.0375, 0.028, 0.0352, 0.0507, 0.038, 0.0598, 0.0581, 0.0565, 0.0479, 0.0511, 0.0376, 0.0534, 0.0585, 0.0506, 0.037, 0.0333, 0.0478, 0.048, 0.0714, 0.0359, 0.0343, 0.0532, 0.0452, 0.0779, 0.0377, 0.0653, 490.7391, 499.8134, 497.2968, 490.421, 513.4861, 505.6125, 0.0405, 0.0628, 0.0416, 0.0634, 0.0518, 0.0421, 0.0507, 0.0415, 0.0325, 0.0638, 0.044, 0.0423, 0.0356, 0.0526, 0.0297, 0.0395, 0.0519, 0.0379, 0.0561, 0.0564, 0.0589, 0.0474, 0.0425, 0.0432, 0.0688, 0.0554, 0.0524, 0.0538, 0.0683, 0.0644, 0.0333, 0.0525, 0.0681, 0.0479, 0.0492, 0.0427, 0.055, 0.0464, 0.0631, 0.0395, 0.0468, 0.0518, 0.0462, 0.0468, 0.0522, 0.0468, 0.0511, 0.0364, 0.0622, 0.0309, 0.0433, 0.064, 0.0284, 0.0709, 0.044, 0.047, 0.0498, 0.0607, 0.0485, 0.0565, 0.0596, 0.0656, 0.0524, 0.0439, 0.0377, 0.0431, 496.7001, 494.1156, 507.7887, 480.3311, 493.4945, 507.9081, 0.0463, 0.0489, 0.0564, 0.0649, 0.0622, 0.0424, 0.0311, 0.0505, 0.0398, 0.0452, 0.0417, 0.0611, 0.0449, 0.048, 0.0596, 0.0543, 0.0473, 0.0527, 0.0654, 0.0582, 0.0485, 0.0548, 0.0517, 0.0635, 0.0465, 0.0562, 0.071, 0.0418, 0.0499, 0.0656, 0.0684, 0.0584, 0.0539, 0.0492, 0.0326, 0.0411, 0.0607, 0.0451, 0.0472, 0.052, 0.0484, 0.0454, 0.028, 0.054, 0.0498, 0.0525, 0.0459, 0.0566, 0.0414, 0.0647, 0.0581, 0.0597, 0.0477, 0.0274, 0.0394, 0.0324, 0.0504, 0.0539, 0.0705, 0.043, 0.0451, 0.0337, 0.0775, 0.0562, 0.0628, 0.0458, 496.8816, 497.3389, 477.0454, 492.3137, 499.6248, 502.896, 0.0616, 0.0526, 0.0438, 0.0414, 0.0436, 0.0684, 0.052, 0.0496, 0.0662, 0.0559, 0.0546, 0.0385, 0.0597, 0.0384, 0.049, 0.0374, 0.0661, 0.0555, 0.0464, 0.04, 0.0451, 0.0524, 0.0303, 0.0581, 0.0595, 0.049, 0.0537, 0.06, 0.0568, 0.0526, 0.0439, 0.0462, 0.0497, 0.0291, 0.0535, 0.0375, 0.0517, 0.0499, 0.0466, 0.032, 0.0546, 0.0524, 0.0513, 0.0527, 0.0414, 0.0618, 0.0503, 0.0583, 0.041, 0.0429, 0.0529, 0.0528, 0.05, 0.0625, 0.0337, 0.0505, 0.0362, 0.0674, 0.0529, 0.0655, 0.0662, 0.0437, 0.0461, 0.064, 0.0422, 0.0487, 496.1863, 488.7987, 520.2551, 507.1485, 496.1513, 486.5951, 0.0487, 0.0566, 0.0678, 0.0707, 0.052, 0.0663, 0.0524, 0.0594, 0.0522, 0.0449, 0.0653, 0.0479, 0.0435, 0.0411, 0.0589, 0.0577, 0.0579, 0.0517, 0.036, 0.0625, 0.057, 0.0541, 0.041, 0.0426, 0.0564, 0.0425, 0.0565, 0.0519, 0.0543, 0.0597, 0.0581, 0.0528, 0.0411, 0.0476, 0.0635, 0.0522, 0.0677, 0.0359, 0.0652, 0.04, 0.0358, 0.0409, 0.0398, 0.0658, 0.0403, 0.0543, 0.0479, 0.0338, 0.0545, 0.0517, 0.0378, 0.0603, 0.0414, 0.0322, 0.0603, 0.0458, 0.0384, 0.0443, 0.0441, 0.0493, 0.0378, 0.0612, 0.0547, 0.0476, 0.0678, 0.0517, 504.0298, 498.7177, 500.0053, 512.5111, 494.7635, 508.5412, 0.0429, 0.0437, 0.0406, 0.0353, 0.0551, 0.0373, 0.0592, 0.0591, 0.0466, 0.0571, 0.0548, 0.06, 0.0396, 0.0541, 0.0612, 0.0476, 0.0539, 0.0442, 0.0721, 0.0585, 0.0477, 0.0635, 0.0486, 0.0533, 0.0573, 0.0654, 0.0467, 0.0699, 0.0598, 0.0601, 0.0584, 0.0611, 0.0547, 0.0515, 0.0647, 0.0536, 0.0584, 0.0399, 0.0653, 0.0541, 0.0647, 0.0559, 0.0496, 0.0577, 0.0414, 0.0322, 0.0404, 0.0627, 0.0425, 0.0445, 0.0596, 0.0551, 0.0427, 0.0495, 0.0456, 0.0586, 0.0546, 0.0407, 0.0372, 0.0341, 0.0555, 0.057, 0.0521, 0.0412, 0.0378, 0.0579, 473.509, 504.9354, 495.7398, 495.9017, 522.1803, 506.3852, 0.0519, 0.0488, 0.0359, 0.0385, 0.0326, 0.0488, 0.0368, 0.0493, 0.0461, 0.0558, 0.0513, 0.0477, 0.0485, 0.0403, 0.054, 0.0499, 0.0707, 0.0609, 0.0519, 0.0617, 0.0387, 0.0443, 0.0359, 0.058, 0.057, 0.04, 0.0607, 0.0536, 0.0393, 0.0483, 0.0477, 0.0689, 0.048, 0.0572, 0.0507, 0.0671, 0.0429, 0.0584, 0.0499, 0.045, 0.0509, 0.0662, 0.0569, 0.0435, 0.0429, 0.0595, 0.0625, 0.0426, 0.0414, 0.0433, 0.0387, 0.0563, 0.0524, 0.0454, 0.0332, 0.0459, 0.0389, 0.0573, 0.055, 0.0559, 0.048, 0.0584, 0.0591, 0.0551, 0.063, 0.0486, 488.4266, 487.2601, 516.5337, 487.9673, 505.05, 500.4938, 0.0489, 0.0627, 0.0649, 0.043, 0.0518, 0.0519, 0.0361, 0.0564, 0.0553, 0.0497, 0.065, 0.0334, 0.0577, 0.0447, 0.0396, 0.0573, 0.0488, 0.0451, 0.0671, 0.0372, 0.0384, 0.0425, 0.053, 0.0499, 0.0493, 0.0407, 0.0403, 0.0532, 0.032, 0.0559, 0.0472, 0.061, 0.0542, 0.0548, 0.0522, 0.0278, 0.0539, 0.0484, 0.0606, 0.0418, 0.0401, 0.0606, 0.0422, 0.0574, 0.0367, 0.0493, 0.0542, 0.0485, 0.0473, 0.0587, 0.0304, 0.0416, 0.0331, 0.0407, 0.0421, 0.0539, 0.0474, 0.0557, 0.0614, 0.0644, 0.0471, 0.02, 0.053, 0.0617, 0.0377, 0.0437, 506.64, 504.7903, 505.8813, 505.423, 480.7013, 507.7932, 0.0446, 0.0717, 0.0667, 0.0488, 0.0632, 0.0432, 0.0543, 0.0553, 0.0589, 0.0358, 0.0713, 0.0537, 0.0621, 0.0469, 0.0504, 0.0489, 0.0511, 0.048, 0.0569, 0.0728, 0.0354, 0.043, 0.0313, 0.0475, 0.0487, 0.0546, 0.0448, 0.0314, 0.04, 0.0536, 0.0484, 0.0699, 0.0662, 0.0493, 0.057, 0.0562, 0.0656, 0.0585, 0.0487, 0.0412, 0.042, 0.0496, 0.0418, 0.0412, 0.0526, 0.0346, 0.041, 0.0709, 0.0288, 0.0594, 0.0524, 0.0416, 0.058, 0.0377, 0.0519, 0.0481, 0.0492, 0.0491, 0.0563, 0.0574, 0.064, 0.0448, 0.0495, 0.0557, 0.0564, 0.035, 498.6466, 506.5146, 517.4506, 482.7378, 510.3746, 486.993, 0.0537, 0.0501, 0.0436, 0.0476, 0.0545, 0.0594, 0.0492, 0.0597, 0.0377, 0.0428, 0.0354, 0.0514, 0.0536, 0.0576, 0.0509, 0.0583, 0.0322, 0.0517, 0.0427, 0.0559, 0.0455, 0.0505, 0.0657, 0.0634, 0.0503, 0.0418, 0.0587, 0.0483, 0.0474, 0.0458, 0.0415, 0.0519, 0.0617, 0.0601, 0.0452, 0.0335, 0.032, 0.0518, 0.0397, 0.0507, 0.0508, 0.0583, 0.029, 0.049, 0.0507, 0.0465, 0.0511, 0.0423, 0.0459, 0.0591, 0.0406, 0.0536, 0.0567, 0.0459, 0.0488, 0.041, 0.0536, 0.0422, 0.039, 0.057, 0.0534, 0.0559, 0.0328, 0.0328, 0.0432, 0.0442, 498.4146, 498.7709, 496.8735, 482.2647, 513.1748, 508.3904, 0.0645, 0.0411, 0.0362, 0.0452, 0.0406, 0.0489, 0.0519, 0.0555, 0.0307, 0.0456, 0.0523, 0.0678, 0.0576, 0.0452, 0.0535, 0.0659, 0.0525, 0.049, 0.0736, 0.053, 0.0581, 0.056, 0.0442, 0.0506, 0.0477, 0.0569, 0.0511, 0.0425, 0.0493, 0.0551, 0.0531, 0.0357, 0.0612, 0.0621, 0.0495, 0.0542, 0.0643, 0.0572, 0.0508, 0.0397, 0.0688, 0.0517, 0.0455, 0.0465, 0.0484, 0.0323, 0.0547, 0.0628, 0.0466, 0.0582, 0.0478, 0.0498, 0.049, 0.0567, 0.0487, 0.0646, 0.0418, 0.0515, 0.0718, 0.0644, 0.0393, 0.0457, 0.0603, 0.0432, 0.0501, 0.0519, 489.9988, 500.1863, 491.2338, 509.019, 505.5364, 504.4816, 0.0599, 0.0484, 0.0511, 0.0595, 0.0368, 0.0445, 0.041, 0.0658, 0.0544, 0.0493, 0.0597, 0.0675, 0.0424, 0.0505, 0.0587, 0.0607, 0.0342, 0.0527, 0.0434, 0.047, 0.0508, 0.0424, 0.0466, 0.0584, 0.0551, 0.0571, 0.0406, 0.0459, 0.0653, 0.0505, 0.0506, 0.0322, 0.0417, 0.0476, 0.0449, 0.0428, 0.0469, 0.0384, 0.0698, 0.0256, 0.0541, 0.0523, 0.047, 0.0408, 0.0503, 0.0441, 0.0499, 0.044, 0.0253, 0.0602, 0.0602, 0.0526, 0.0586, 0.0658, 0.0334, 0.0513, 0.0403, 0.0471, 0.0469, 0.0602, 0.0438, 0.0717, 0.0446, 0.0391, 0.0428, 0.0436, 496.7591, 495.5263, 493.3084, 499.0154, 488.0579, 489.6043, 0.0424, 0.0491, 0.0272, 0.0554, 0.0643, 0.0429, 0.0532, 0.0457, 0.0314, 0.0471, 0.0604, 0.0506, 0.0522, 0.053, 0.0586, 0.0488, 0.0559, 0.0642, 0.0413, 0.0479, 0.0604, 0.0542, 0.0552, 0.0577, 0.0433, 0.0474, 0.0342, 0.0334, 0.0471, 0.0683, 0.059, 0.0402, 0.0452, 0.0631, 0.0437, 0.0544, 0.0358, 0.0463, 0.0551, 0.0527, 0.049, 0.042, 0.0496, 0.0546, 0.0539, 0.0373, 0.0509, 0.0411, 0.0518, 0.0367, 0.0469, 0.0679, 0.0603, 0.0393, 0.044, 0.051, 0.0563, 0.0206, 0.0651, 0.0598, 0.0536, 0.0548, 0.0562, 0.0437, 0.0526, 0.048, 489.2656, 499.2091, 495.4614, 492.343, 495.0047, 499.9882, 0.041, 0.0464, 0.0415, 0.0575, 0.0454, 0.0612, 0.0297, 0.046, 0.0637, 0.0291, 0.0678, 0.0436, 0.0454, 0.0314, 0.0392, 0.0457, 0.0537, 0.0352, 0.04, 0.0462, 0.0487, 0.0518, 0.0426, 0.0584, 0.0422, 0.0358, 0.0529, 0.0625, 0.0737, 0.0497, 0.0454, 0.0542, 0.05, 0.0485, 0.0386, 0.0543, 0.0586, 0.0503, 0.058, 0.0553, 0.0495, 0.0726, 0.048, 0.0484, 0.0482, 0.0613, 0.0648, 0.0542, 0.0497, 0.064, 0.0683, 0.0466, 0.065, 0.0326, 0.0641, 0.0498, 0.0387, 0.0507, 0.0618, 0.0382, 0.051, 0.0474, 0.0709, 0.0538, 0.0593, 0.0634, 503.6594, 496.1477, 483.0803, 509.361, 513.3712, 504.9136, 0.0542, 0.0517, 0.0225, 0.0422, 0.0547, 0.063, 0.069, 0.052, 0.055, 0.0429, 0.0445, 0.029, 0.0295, 0.0375, 0.0264, 0.0476, 0.0344, 0.0609, 0.0482, 0.0468, 0.0554, 0.0395, 0.0494, 0.0458, 0.0425, 0.0528, 0.0538, 0.0613, 0.0577, 0.0647, 0.0504, 0.0493, 0.0486, 0.0592, 0.043, 0.0609, 0.0457, 0.0378, 0.0452, 0.0511, 0.0636, 0.0587, 0.0553, 0.0623, 0.0667, 0.0464, 0.0517, 0.0432, 0.0471, 0.0529, 0.0488, 0.0426, 0.0575, 0.0378, 0.0553, 0.0462, 0.0502, 0.0447, 0.0648, 0.0551, 0.0524, 0.0549, 0.0471, 0.0566, 0.0578, 0.0692, 503.6963, 507.4987, 497.4635, 492.5825, 486.8782, 500.835, 0.0417, 0.0617, 0.0366, 0.0544, 0.0513, 0.0476, 0.055, 0.0415, 0.0433, 0.0484, 0.0473, 0.0412, 0.0567, 0.0335, 0.0541, 0.065, 0.0312, 0.0496, 0.0512, 0.0458, 0.0511, 0.0424, 0.0475, 0.0505, 0.061, 0.0597, 0.0521, 0.0551, 0.0418, 0.045, 0.0404, 0.0592, 0.0652, 0.0533, 0.0361, 0.0353, 0.0604, 0.0505, 0.0427, 0.0624, 0.0679, 0.063, 0.0461, 0.0605, 0.0508, 0.0519, 0.054, 0.0397, 0.0405, 0.0451, 0.0347, 0.0612, 0.0498, 0.0597, 0.0421, 0.0628, 0.0418, 0.0753, 0.0622, 0.0382, 0.0597, 0.0431, 0.0348, 0.0582, 0.0597, 0.0471, 511.7898, 502.1383, 518.431, 490.2196, 523.3564, 494.8155, 0.0585, 0.0754, 0.0501, 0.0447, 0.0373, 0.072, 0.0585, 0.0484, 0.0518, 0.0321, 0.0442, 0.0515, 0.0556, 0.05, 0.0563, 0.0443, 0.0564, 0.0536, 0.0513, 0.0542, 0.0505, 0.0652, 0.0555, 0.042, 0.0409, 0.0513, 0.035, 0.0528, 0.043, 0.029, 0.0407, 0.0336, 0.0454, 0.0337, 0.0543, 0.053, 0.0504, 0.0447, 0.0531, 0.0289, 0.0569, 0.0353, 0.0411, 0.0546, 0.0604, 0.055, 0.0492, 0.0367, 0.0524, 0.0635, 0.0511, 0.0405, 0.04, 0.0657, 0.0372, 0.0393, 0.0624, 0.0426, 0.0542, 0.0549, 0.0526, 0.0604, 0.0422, 0.0623, 0.0427, 0.0537, 485.9407, 512.1127, 524.6268, 505.4766, 494.7318, 502.8218, 0.0422, 0.0327, 0.0459, 0.0509, 0.0482, 0.0457, 0.0617, 0.0363, 0.0587, 0.0507, 0.0412, 0.0455, 0.0389, 0.0532, 0.0598, 0.0475, 0.0511, 0.0416, 0.0492, 0.0642, 0.0357, 0.0391, 0.0408, 0.053, 0.0411, 0.0515, 0.0407, 0.0479, 0.0562, 0.0226, 0.0404, 0.0481, 0.0474, 0.0449, 0.0379, 0.0699, 0.0444, 0.0464, 0.0562, 0.0351, 0.047, 0.0509, 0.049, 0.055, 0.0405, 0.0506, 0.0617, 0.0402, 0.0499, 0.0591, 0.0447, 0.0677, 0.0439, 0.0483, 0.0488, 0.0515, 0.0436, 0.0627, 0.0422, 0.0447, 0.066, 0.0537, 0.05, 0.0542, 0.0633, 0.059, 492.8725, 492.1254, 495.8702, 526.4009, 497.202, 493.0617, 0.0587, 0.0331, 0.0487, 0.0379, 0.0461, 0.051, 0.0461, 0.054, 0.0641, 0.0479, 0.0572, 0.0434, 0.054, 0.0495, 0.0649, 0.0427, 0.0481, 0.051, 0.0573, 0.0476, 0.0381, 0.033, 0.0555, 0.0409, 0.0549, 0.0544, 0.0494, 0.0585, 0.0305, 0.0483, 0.0507, 0.0679, 0.042, 0.058, 0.0507, 0.0393, 0.0497, 0.0326, 0.0475, 0.0647, 0.0326, 0.0366, 0.0465, 0.0428, 0.0484, 0.0511, 0.0455, 0.0527, 0.0486, 0.0634, 0.0587, 0.0487, 0.0503, 0.0519, 0.0484, 0.0532, 0.0463, 0.0653, 0.0562, 0.0382, 0.0441, 0.0433, 0.0323, 0.0648, 0.0591, 0.0507, 493.8295, 508.914, 483.9943, 503.9014, 503.7869, 514.9981, 0.0464, 0.0432, 0.0692, 0.0539, 0.0638, 0.0472, 0.0415, 0.0465, 0.0576, 0.0779, 0.0443, 0.0561, 0.044, 0.0637, 0.0638, 0.0526, 0.0547, 0.0581, 0.0199, 0.0638, 0.0766, 0.0539, 0.0391, 0.046, 0.0522, 0.0724, 0.0593, 0.0601, 0.0526, 0.0463, 0.0562, 0.0805, 0.0443, 0.0473, 0.0594, 0.0646, 0.049, 0.0399, 0.036, 0.0591, 0.0382, 0.0567, 0.046, 0.0586, 0.0664, 0.0628, 0.0664, 0.0458, 0.0449, 0.0613, 0.0346, 0.0624, 0.0388, 0.0504, 0.055, 0.0403, 0.0556, 0.0471, 0.0471, 0.062, 0.0444, 0.0596, 0.0602, 0.0693, 0.0392, 0.0597, 510.1432, 498.3443, 490.9076, 489.4998, 508.9621, 510.6375, 0.0635, 0.0499, 0.0473, 0.026, 0.0466, 0.0568, 0.0398, 0.0452, 0.0581, 0.0552, 0.0491, 0.0293, 0.0428, 0.0396, 0.0627, 0.0353, 0.0551, 0.0581, 0.0544, 0.0456, 0.0708, 0.0413, 0.0463, 0.0575, 0.0554, 0.0458, 0.0427, 0.0518, 0.0259, 0.0317, 0.0578, 0.0243, 0.0451, 0.05, 0.0429, 0.0445, 0.0552, 0.0472, 0.0251, 0.056, 0.0526, 0.0501, 0.0411, 0.032, 0.0699, 0.0494, 0.0594, 0.0544, 0.049, 0.0658, 0.0625, 0.0619, 0.0426, 0.0401, 0.0613, 0.0517, 0.0488, 0.0418, 0.0351, 0.0611, 0.0511, 0.0406, 0.0475, 0.0383, 0.0438, 0.0415, 503.9959, 503.9489, 498.5347, 518.6201, 500.1094, 495.1321, 0.055, 0.059, 0.0382, 0.0687, 0.0519, 0.0434, 0.0617, 0.0582, 0.0691, 0.0596, 0.0425, 0.0504, 0.0495, 0.0583, 0.0335, 0.056, 0.046, 0.0377, 0.036, 0.0405, 0.0549, 0.0566, 0.0517, 0.0595, 0.0446, 0.0563, 0.0555, 0.048, 0.0424, 0.0415, 0.0422, 0.0531, 0.0494, 0.0346, 0.055, 0.0566, 0.0394, 0.0595, 0.0568, 0.0512, 0.0457, 0.0625, 0.0491, 0.066, 0.049, 0.0167, 0.0385, 0.0577, 0.0544, 0.0581, 0.0609, 0.0662, 0.0462, 0.0589, 0.0576, 0.0414, 0.0559, 0.0607, 0.0596, 0.069, 0.0741, 0.0522, 0.0453, 0.0572, 0.0513, 0.058, 503.0903, 482.625, 494.4807, 492.4037, 478.6338, 505.5831, 0.0659, 0.0394, 0.0599, 0.0356, 0.0429, 0.0489, 0.0543, 0.0641, 0.0463, 0.0527, 0.0482, 0.0533, 0.0383, 0.0459, 0.0489, 0.0297, 0.0533, 0.0494, 0.0431, 0.0469, 0.0494, 0.0664, 0.0446, 0.0365, 0.0505, 0.0653, 0.0472, 0.0508, 0.0402, 0.042, 0.0555, 0.0535, 0.0518, 0.0468, 0.06, 0.043, 0.0567, 0.0527, 0.0387, 0.0432, 0.0379, 0.0528, 0.0517, 0.0414, 0.0456, 0.0451, 0.0531, 0.0528, 0.0541, 0.0618, 0.0525, 0.0613, 0.038, 0.0444, 0.0531, 0.0463, 0.0631, 0.0387, 0.0562, 0.067, 0.0417, 0.0427, 0.0668, 0.0501, 0.0406, 0.0431, 505.2994, 510.6529, 475.5016, 497.8084, 493.8334, 519.1271, 0.05, 0.058, 0.042, 0.0468, 0.0244, 0.0601, 0.0609, 0.0461, 0.0386, 0.0745, 0.0441, 0.0511, 0.0597, 0.0543, 0.0423, 0.0714, 0.0366, 0.055, 0.0438, 0.0485, 0.0531, 0.0333, 0.0527, 0.0615, 0.0448, 0.0611, 0.0567, 0.0551, 0.04, 0.0417, 0.0533, 0.0548, 0.0518, 0.0325, 0.0582, 0.039, 0.0638, 0.0709, 0.0645, 0.0483, 0.0542, 0.0331, 0.0472, 0.0731, 0.0306, 0.0534, 0.0481, 0.0553, 0.0453, 0.0581, 0.0497, 0.0391, 0.0411, 0.0545, 0.0353, 0.0584, 0.0283, 0.0649, 0.039, 0.0644, 0.0669, 0.0473, 0.0655, 0.0519, 0.0541, 0.0653, 497.3139, 509.2897, 492.282, 515.9198, 494.1337, 511.3917, 0.0522, 0.0566, 0.0303, 0.0496, 0.0558, 0.0546, 0.0448, 0.0602, 0.048, 0.0389, 0.053, 0.0494, 0.0418, 0.0371, 0.0597, 0.0437, 0.0474, 0.0634, 0.0467, 0.0533, 0.0449, 0.0438, 0.0345, 0.0666, 0.0541, 0.0534, 0.05, 0.0411, 0.0516, 0.0398, 0.0533, 0.0547, 0.048, 0.0564, 0.0564, 0.0539, 0.0348, 0.0425, 0.0492, 0.0623, 0.0495, 0.052, 0.0505, 0.0463, 0.0535, 0.0346, 0.0423, 0.068, 0.059, 0.0439, 0.0548, 0.0458, 0.0391, 0.0522, 0.0425, 0.063, 0.0478, 0.0426, 0.05, 0.0478, 0.0575, 0.057, 0.0408, 0.057, 0.0617, 0.0536, 510.2362, 493.7698, 510.6668, 501.0896, 511.6058, 502.3718, 0.0553, 0.0331, 0.0369, 0.0507, 0.0392, 0.0378, 0.0252, 0.0392, 0.0524, 0.0379, 0.0558, 0.0315, 0.0693, 0.0618, 0.0257, 0.0299, 0.0502, 0.0329, 0.0427, 0.0534, 0.0303, 0.0562, 0.049, 0.0355, 0.063, 0.0564, 0.0434, 0.0603, 0.0435, 0.0327, 0.0481, 0.0509, 0.049, 0.0495, 0.0509, 0.0483, 0.0516, 0.0604, 0.0496, 0.0571, 0.0459, 0.0421, 0.0357, 0.0539, 0.0516, 0.037, 0.0374, 0.0663, 0.0539, 0.0457, 0.051, 0.0433, 0.051, 0.0521, 0.0345, 0.0385, 0.0523, 0.0377, 0.0585, 0.0475, 0.0471, 0.0566, 0.0522, 0.0582, 0.0429, 0.0536, 506.4458, 497.273, 496.6595, 504.2874, 508.6625, 493.6127, 0.0549, 0.0416, 0.0345, 0.0557, 0.0494, 0.0402, 0.0405, 0.0533, 0.0527, 0.0594, 0.0517, 0.0459, 0.06, 0.0697, 0.0492, 0.045, 0.0502, 0.0449, 0.0364, 0.0509, 0.05, 0.0413, 0.0605, 0.077, 0.0529, 0.041, 0.0498, 0.0549, 0.0488, 0.0503, 0.0533, 0.0405, 0.0559, 0.0427, 0.0679, 0.0436, 0.0322, 0.0578, 0.0374, 0.0883, 0.0624, 0.0573, 0.0413, 0.0572, 0.0576, 0.0476, 0.0447, 0.0429, 0.0548, 0.0559, 0.0452, 0.0479, 0.0586, 0.0628, 0.0503, 0.0465, 0.0565, 0.031, 0.0449, 0.0418, 0.045, 0.053, 0.061, 0.0707, 0.0661, 0.0464, 502.3425, 494.1181, 503.4898, 496.6846, 506.1382, 509.9988, 0.0452, 0.0632, 0.0566, 0.0458, 0.0424, 0.0658, 0.0397, 0.0683, 0.0698, 0.0543, 0.0526, 0.0484, 0.0403, 0.0594, 0.0442, 0.0491, 0.0712, 0.052, 0.0749, 0.0663, 0.0586, 0.0574, 0.0696, 0.0479, 0.0448, 0.0488, 0.0387, 0.0414, 0.0458, 0.0604, 0.0543, 0.047, 0.0447, 0.0421, 0.038, 0.0396, 0.0335, 0.0523, 0.054, 0.0447, 0.051, 0.0552, 0.0567, 0.0475, 0.0539, 0.0522, 0.0328, 0.0619, 0.0557, 0.0544, 0.0559, 0.0635, 0.0372, 0.0738, 0.0441, 0.0394, 0.0317, 0.0403, 0.0503, 0.0447, 0.037, 0.0569, 0.0295, 0.0388, 0.0712, 0.0546, 497.6464, 502.0742, 502.0393, 506.0052, 501.6415, 502.0609, 0.0516, 0.0443, 0.0443, 0.0617, 0.0409, 0.0509, 0.0373, 0.0367, 0.0403, 0.0449, 0.0531, 0.0636, 0.0452, 0.0486, 0.0469, 0.0668, 0.0581, 0.0458, 0.056, 0.0452, 0.0541, 0.0392, 0.0359, 0.0455, 0.0455, 0.042, 0.0464, 0.0496, 0.0405, 0.0607, 0.0551, 0.0489, 0.0516, 0.0609, 0.0597, 0.0581, 0.0442, 0.0695, 0.0462, 0.06, 0.0541, 0.0496, 0.0579, 0.0412, 0.031, 0.0643, 0.0566, 0.0499, 0.0538, 0.044, 0.0662, 0.0517, 0.0418, 0.0608, 0.0615, 0.0662, 0.0323, 0.0472, 0.0453, 0.0555, 0.0616, 0.046, 0.0468, 0.0533, 0.0572, 0.0622, 485.8171, 486.9826, 500.0398, 494.2567, 512.8219, 495.1266, 0.0551, 0.0437, 0.0736, 0.0438, 0.0438, 0.0706, 0.0708, 0.0463, 0.0447, 0.0511, 0.0502, 0.0499, 0.0499, 0.0627, 0.0415, 0.071, 0.0308, 0.0335, 0.0596, 0.0537, 0.0615, 0.0646, 0.0517, 0.0319, 0.0571, 0.0477, 0.0533, 0.036, 0.0449, 0.0541, 0.0519, 0.0467, 0.0578, 0.0482, 0.054, 0.0468, 0.0484, 0.026, 0.0563, 0.0588, 0.0421, 0.0351, 0.055, 0.0503, 0.0483, 0.0543, 0.0422, 0.0418, 0.0455, 0.0429, 0.0617, 0.0616, 0.0463, 0.0511, 0.0718, 0.0471, 0.0457, 0.0567, 0.0598, 0.0493, 0.0538, 0.0469, 0.037, 0.0528, 0.0609, 0.0392, 504.6474, 483.7968, 500.7402, 504.6047, 513.5971, 499.7954, 0.0423, 0.0445, 0.0601, 0.0392, 0.0633, 0.028, 0.0374, 0.0567, 0.0554, 0.0439, 0.0487, 0.0421, 0.0499, 0.0619, 0.0333, 0.0592, 0.0541, 0.0496, 0.0646, 0.0607, 0.0505, 0.0617, 0.0376, 0.042, 0.0638, 0.0606, 0.0467, 0.0376, 0.0551, 0.0417, 0.0274, 0.0473, 0.04, 0.0591, 0.035, 0.0404, 0.0626, 0.0463, 0.0626, 0.0614, 0.0404, 0.0615, 0.0586, 0.0441, 0.037, 0.0602, 0.0399, 0.0644, 0.0578, 0.0395, 0.0265, 0.0466, 0.0554, 0.037, 0.0526, 0.0354, 0.0628, 0.0521, 0.0739, 0.0537, 0.0683, 0.0631, 0.0552, 0.0347, 0.0545, 0.0438, 503.8247, 491.651, 507.4585, 498.7504, 497.0486, 501.0415, 0.0204, 0.058, 0.0485, 0.0677, 0.0543, 0.0666, 0.0416, 0.0474, 0.0425, 0.0554, 0.0577, 0.0323, 0.0556, 0.0429, 0.0378, 0.039, 0.0537, 0.0438, 0.0522, 0.0459, 0.0475, 0.0359, 0.0455, 0.0608, 0.0538, 0.059, 0.0486, 0.0656, 0.0393, 0.0646, 0.0633, 0.0398, 0.0585, 0.0478, 0.0601, 0.0585, 0.063, 0.0416, 0.0344, 0.0438, 0.0402, 0.0476, 0.0478, 0.0437, 0.0614, 0.0573, 0.0573, 0.0429, 0.0487, 0.0658, 0.0402, 0.0479, 0.0541, 0.0478, 0.0677, 0.045, 0.0563, 0.0446, 0.0463, 0.0482, 0.0608, 0.05, 0.0622, 0.0532, 0.046, 0.0431] \ No newline at end of file diff --git a/skills/cloud/agent-platform-alert-configuration/assets/mock_seasonal.json b/skills/cloud/agent-platform-alert-configuration/assets/mock_seasonal.json new file mode 100644 index 0000000000..bc79a4e1cf --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/assets/mock_seasonal.json @@ -0,0 +1 @@ +[4.7839, 4.7514, 4.8759, 6.1493, 4.9799, 3.0214, 5.8835, 5.1227, 5.3582, 6.0385, 6.4151, 8.0349, 7.5183, 6.9635, 5.9753, 5.8662, 8.0833, 10.0263, 8.4879, 8.6515, 10.0138, 7.4604, 9.6161, 11.2828, 12.339, 11.168, 12.6123, 12.9562, 14.3116, 12.0402, 15.1515, 12.6334, 11.5981, 15.2569, 15.4488, 18.8076, 19.1766, 17.0533, 20.8695, 18.8261, 20.9452, 21.3926, 22.7774, 24.619, 25.1467, 25.5242, 26.7964, 27.3729, 26.5595, 27.2794, 28.5164, 30.846, 30.607, 35.38, 31.5503, 32.042, 35.7618, 37.668, 37.2268, 38.6614, 40.4927, 39.1631, 38.1258, 40.4228, 43.6152, 40.9889, 44.1766, 45.4808, 45.6046, 48.1423, 48.9083, 52.5004, 50.93, 50.0676, 51.1202, 51.6958, 55.3504, 54.0488, 55.7683, 57.9695, 56.729, 58.3386, 56.9779, 59.0721, 60.7953, 63.2159, 65.322, 64.4371, 65.783, 66.5617, 68.8479, 69.4636, 69.4284, 68.3866, 72.1338, 72.2161, 74.3404, 73.2999, 77.1081, 74.4623, 78.2005, 76.7816, 76.6199, 76.4739, 78.6989, 81.8055, 81.6261, 82.1514, 78.256, 83.5728, 84.0113, 83.0079, 83.5309, 85.0913, 88.2882, 84.7074, 86.2201, 89.4588, 87.2834, 87.9247, 89.1178, 87.5908, 90.2454, 88.5448, 92.1116, 91.1938, 94.9997, 92.3616, 94.3347, 90.657, 92.7341, 93.687, 96.0852, 91.189, 95.4192, 95.0224, 96.6167, 95.5448, 94.6931, 93.9501, 92.9565, 95.1968, 94.6696, 98.0197, 94.0835, 95.4699, 92.6037, 94.3105, 95.2205, 95.9685, 96.7872, 94.41, 92.6405, 94.8221, 94.7088, 94.4479, 92.4162, 94.9023, 93.0491, 93.6616, 94.1973, 92.8543, 92.0044, 94.4182, 91.1494, 89.9153, 90.0834, 91.6769, 89.1492, 89.2501, 89.7462, 86.6468, 84.272, 86.7391, 86.0455, 84.1828, 85.7786, 84.7441, 81.685, 83.2837, 81.5256, 78.8935, 81.081, 79.6027, 77.8075, 78.951, 78.1264, 75.7426, 76.4115, 76.5233, 73.1095, 73.9161, 72.5041, 75.0435, 67.9852, 70.9459, 68.5585, 67.9733, 70.0731, 66.2969, 68.7523, 63.8016, 64.0575, 61.856, 60.6108, 60.7896, 59.3159, 59.0828, 54.6566, 59.8493, 55.6232, 57.5134, 52.4113, 53.3819, 56.7103, 49.6771, 47.7571, 48.1986, 48.7459, 48.0969, 48.0135, 44.7155, 41.7229, 42.5019, 44.0462, 41.9194, 37.3248, 39.2516, 40.4504, 40.5975, 37.3108, 35.9992, 32.7488, 32.4254, 32.8496, 32.6312, 31.8525, 29.3797, 27.4934, 27.1888, 25.7645, 27.6203, 22.3531, 24.4994, 24.8639, 25.6741, 22.7127, 23.3354, 20.4761, 19.2128, 18.5869, 21.1729, 19.6715, 18.2247, 21.8179, 16.1187, 16.4585, 15.3666, 13.9663, 17.1842, 15.3981, 10.4846, 11.4476, 12.1715, 12.2036, 8.5246, 6.7012, 6.7812, 9.1095, 8.6562, 8.9433, 6.9051, 5.8775, 4.3536, 7.5765, 7.3528, 8.0228, 7.4758, 5.7806, 7.8886, 5.4791, 4.5325, 4.6041, 4.3902, 1.9226, 5.3308, 5.4177, 4.4673, 3.9292, 5.5684, 7.6181, 5.1537, 4.4098, 4.3803, 5.2803, 3.6266, 5.4958, 5.9564, 8.8337, 7.7048, 8.0844, 5.7283, 8.0876, 5.6791, 8.1818, 8.6878, 7.2758, 11.8101, 10.0672, 6.7866, 10.9138, 9.9362, 11.0309, 12.2276, 12.6332, 9.5251, 11.4007, 14.8689, 16.2509, 17.7599, 18.2328, 13.0839, 17.9767, 14.6192, 20.6006, 19.2619, 17.8867, 23.3443, 22.0405, 18.9967, 23.0459, 22.2944, 24.356, 24.3015, 27.8818, 24.5203, 27.9761, 31.0768, 27.9809, 30.413, 31.2852, 30.8418, 33.7511, 33.8359, 33.1934, 38.1782, 37.6557, 37.157, 37.3981, 38.1276, 42.1066, 41.0012, 42.875, 42.8778, 45.0509, 44.927, 47.2238, 46.782, 49.2815, 50.0269, 49.8251, 49.692, 50.5527, 51.832, 52.4453, 56.1977, 55.8038, 59.2007, 61.2332, 58.8154, 57.4178, 60.1237, 61.5338, 60.3596, 63.156, 64.8952, 64.7018, 65.1571, 66.0826, 70.8036, 68.7784, 68.6581, 70.2193, 73.076, 71.473, 74.0676, 75.2703, 76.1243, 77.9105, 75.7832, 79.0934, 76.9773, 78.3306, 81.4647, 81.6351, 81.1212, 80.0327, 83.3489, 82.2319, 82.447, 83.4998, 85.4645, 82.5194, 86.8312, 87.1227, 86.5393, 89.508, 89.3882, 88.0617, 88.4477, 90.9582, 88.0852, 90.2849, 90.2424, 91.6794, 92.257, 92.3413, 94.3141, 93.2594, 92.7298, 91.6504, 94.7932, 94.7255, 96.3116, 93.1952, 94.5159, 94.527, 94.6011, 94.8443, 92.3318, 96.1946, 95.9691, 96.6957, 98.3637, 94.4787, 94.8494, 94.8434, 97.6458, 92.0182, 95.2049, 92.1101, 90.263, 90.9021, 91.6377, 95.0702, 91.8997, 92.5761, 90.8666, 93.2026, 90.2874, 93.5511, 89.6994, 89.6366, 90.5434, 89.9526, 91.5897, 89.1518, 86.6593, 87.1121, 86.557, 87.9494, 86.8657, 85.5218, 86.3992, 83.6793, 83.6284, 83.4486, 84.7987, 81.672, 80.5919, 78.8328, 80.6996, 78.044, 78.6821, 74.5418, 78.1737, 73.813, 73.8346, 75.7664, 71.3431, 72.3808, 71.7559, 69.2809, 71.9174, 71.266, 65.1662, 65.0473, 67.5221, 63.4364, 63.8818, 63.7261, 63.6301, 61.312, 60.8387, 58.5747, 60.7176, 57.0712, 57.3552, 55.1062, 55.0585, 53.6698, 52.6994, 50.4422, 52.1763, 52.1211, 49.3831, 49.1356, 48.314, 47.47, 45.556, 44.4447, 44.4505, 42.2891, 39.7601, 40.296, 36.4715, 38.3443, 38.0278, 38.3222, 35.7621, 36.0013, 33.6895, 31.244, 29.7593, 29.5276, 28.0478, 30.1719, 28.0441, 28.3555, 26.7654, 27.3876, 26.5896, 23.2211, 22.9796, 21.8065, 19.4015, 22.0874, 22.1126, 19.5888, 18.1421, 19.8166, 13.9762, 17.0887, 16.3821, 14.6717, 16.9926, 15.7552, 13.3183, 13.0081, 12.0018, 11.2792, 9.5667, 13.0988, 12.0893, 11.2818, 7.9992, 12.3374, 11.7812, 8.7075, 6.7033, 6.4142, 8.016, 5.7132, 6.7464, 8.1663, 4.0294, 9.0753, 8.2147, 3.828, 3.4033, 4.9598, 3.7207, 4.7757, 1.2246, 4.6998, 7.715, 3.3251, 4.6011, 2.8084, 6.0537, 4.8333, 8.1786, 5.6952, 4.9655, 6.5091, 5.1053, 4.0577, 6.7425, 6.271, 7.0472, 7.6459, 6.6227, 8.3462, 10.6662, 6.7637, 11.4171, 8.8519, 8.4908, 13.7253, 10.7798, 13.0845, 12.7554, 9.5265, 12.7651, 12.3502, 11.6973, 11.6415, 15.0586, 16.4134, 16.7532, 16.3638, 16.2958, 16.6999, 18.0492, 18.0489, 20.0245, 21.0402, 20.4895, 22.2577, 22.2506, 24.1824, 25.76, 26.0887, 27.3634, 25.022, 27.7875, 29.6364, 31.0588, 28.5852, 32.1152, 34.5889, 33.4224, 34.467, 36.2109, 36.8389, 38.4197, 38.4618, 39.5329, 39.0866, 40.9653, 44.0655, 42.8482, 43.8735, 44.3637, 47.2588, 45.7748, 49.4029, 47.6129, 48.4133, 51.2906, 52.1581, 53.3895, 55.5158, 54.2803, 56.5382, 55.4808, 59.7029, 59.7278, 60.5446, 60.9777, 60.4685, 62.1012, 64.2844, 64.436, 65.5765, 64.27, 67.4924, 67.0857, 67.3928, 69.671, 67.9939, 72.6923, 73.778, 73.2646, 72.6878, 73.5826, 75.5843, 72.9733, 77.665, 79.1632, 78.4576, 78.929, 80.6745, 81.6441, 83.461, 82.534, 82.1428, 83.1409, 84.7449, 87.4291, 85.2205, 84.7112, 85.5567, 84.2716, 88.4473, 86.429, 87.1209, 86.4887, 88.2385, 91.2119, 88.8168, 88.5646, 91.842, 91.4286, 92.9345, 93.7227, 92.9045, 91.2556, 93.281, 94.4608, 93.0588, 91.9791, 95.9703, 93.8534, 94.0012, 94.7565, 96.3745, 95.2991, 94.4573, 96.0125, 98.2205, 97.071, 95.9665, 93.2202, 100.4568, 92.9345, 94.0573, 93.4264, 96.7567, 94.7657, 91.7926, 94.1118, 94.287, 92.7245, 91.087, 91.5243, 91.6309, 91.9745, 90.7947, 92.2918, 89.3589, 87.7622, 88.8064, 91.476, 88.1114, 91.9822, 88.6898, 85.6695, 85.6104, 86.6551, 83.9344, 88.6567, 82.9857, 84.8542, 85.3677, 82.3426, 80.326, 80.7085, 80.9449, 80.9842, 79.0105, 76.0662, 77.4578, 75.5989, 72.5361, 73.9543, 72.0298, 74.2251, 75.4162, 74.4326, 69.9189, 69.2019, 70.051, 69.7155, 66.5926, 66.0958, 68.7013, 64.3036, 62.8038, 62.7722, 62.5164, 59.1634, 59.3672, 57.0295, 57.3968, 59.6609, 56.9863, 54.7602, 52.5223, 52.476, 53.539, 50.1391, 52.1756, 46.9579, 47.6021, 49.671, 43.0662, 46.8836, 41.3493, 43.0193, 41.927, 42.4574, 35.5284, 36.4274, 37.5041, 37.463, 35.1284, 35.2039, 32.8298, 33.0884, 31.5091, 32.2424, 31.8543, 32.2464, 28.2367, 27.8785, 27.5716, 26.2128, 25.7435, 22.6561, 24.7467, 22.0183, 21.1703, 19.4513, 22.8161, 20.1185, 21.3227, 19.2904, 15.6252, 15.8681, 17.9594, 15.0187, 13.8925, 14.6361, 14.0336, 12.1922, 12.6489, 11.8661, 8.1624, 12.283, 9.8118, 12.504, 8.3593, 7.2713, 10.9976, 6.4334, 7.4712, 8.9557, 7.2194, 4.9049, 7.3648, 6.5063, 8.4591, 7.1726, 10.6203, 3.3958, 5.6511, 3.9238, 7.1171, 6.5002, 6.3023, 3.9304, 3.9251, 1.9662, 7.5643, 5.773, 4.6515, 6.4966, 6.2976, 6.7304, 2.9497, 4.453, 5.9768, 6.3978, 7.5025, 4.7767, 6.7242, 10.1058, 8.1206, 8.694, 7.6851, 9.2111, 11.1314, 8.9709, 11.0521, 8.328, 10.9697, 11.9036, 9.9009, 13.1762, 11.0345, 11.376, 13.1364, 16.608, 14.7628, 16.3089, 14.4303, 16.2835, 15.0577, 16.637, 19.4337, 18.8243, 21.7754, 20.0258, 20.7777, 23.7485, 21.6328, 21.1114, 26.141, 25.6206, 22.5737, 26.722, 26.8943, 30.9728, 28.1278, 29.2259, 29.6166, 30.2953, 31.7285, 31.5364, 33.0623, 33.1253, 34.2223, 35.0682, 37.5714, 38.2786, 38.4205, 41.9383, 42.1137, 42.5065, 43.4304, 47.4903, 46.4377, 47.7699, 44.1581, 48.0377, 46.7435, 50.3424, 52.5228, 51.9317, 54.6999, 53.3649, 56.7374, 54.5151, 57.4216, 59.4088, 60.5437, 60.505, 64.5564, 61.9707, 62.8553, 60.3255, 68.0443, 65.7562, 65.4567, 70.105, 70.8711, 70.0655, 68.6263, 72.273, 71.5679, 71.5886, 75.7435, 71.8018, 76.6617, 77.298, 80.4708, 78.6543, 75.8378, 78.4164, 80.4986, 81.6514, 83.291, 79.5669, 82.6826, 85.2876, 86.788, 87.8863, 85.1156, 87.9364, 89.4294, 89.2132, 89.0381, 89.7127, 87.7863, 87.2818, 91.3898, 89.8387, 90.7417, 90.3209, 90.9852, 92.2895, 90.7534, 92.7146, 93.2863, 93.0862, 92.3512, 94.8903, 96.7423, 93.2335, 96.168, 95.8102, 90.6351, 93.5907, 93.875, 96.0763, 95.2399, 95.7009, 95.9544, 93.6409, 93.8373, 93.0693, 94.5734, 94.5539, 94.8718, 93.3853, 95.6761, 95.511, 96.2531, 94.7774, 93.7449, 94.2109, 93.8319, 91.6341, 94.2202, 94.5181, 94.54, 91.2419, 91.2703, 90.9838, 89.9117, 90.3816, 88.7894, 88.1804, 90.5911, 87.6632, 86.9624, 87.613, 85.9264, 85.5322, 84.3767, 82.0022, 83.131, 80.8011, 82.135, 79.3459, 81.7766, 80.7944, 79.3461, 78.0164, 78.0187, 73.6614, 74.1657, 74.7776, 74.6964, 73.2257, 70.9931, 71.8447, 70.0981, 68.3001, 69.5121, 67.8086, 69.0155, 67.308, 66.4929, 64.9473, 62.264, 61.1889, 59.4401, 65.0144, 58.1206, 58.4643, 57.2736, 56.4239, 54.9976, 52.0965, 52.4247, 50.8571, 51.7727, 49.9554, 48.9692, 49.3421, 47.1629, 48.455, 45.4987, 45.4387, 42.1384, 44.1398, 41.5781, 41.8341, 40.7552, 39.044, 38.6793, 36.8297, 38.4874, 36.9367, 35.3328, 31.9201, 31.1303, 30.8806, 30.6147, 30.769, 27.8765, 30.1211, 27.5054, 23.636, 25.1085, 21.1216, 23.0615, 22.5674, 21.6901, 21.3456, 20.4834, 19.4388, 21.8522, 20.7043, 16.4674, 19.3745, 14.3905, 14.9453, 17.8158, 11.5459, 15.1613, 11.8298, 12.9222, 12.3136, 14.5932, 10.3131, 10.9253, 11.4878, 9.7068, 8.4076, 9.1273, 8.9513, 9.7515, 6.6451, 7.0401, 7.8726, 6.832, 6.5198, 6.5078, 5.2458, 4.8671, 6.5258, 4.041, 7.2718, 5.7109, 3.3527, 5.5958, 5.3473, 6.1861, 6.749, 4.4504, 3.9491, 4.1339, 3.6247, 4.0516, 5.6942, 5.4079, 4.7867, 4.5763, 6.3865, 5.3143, 4.7294, 6.3058, 6.401, 5.4777, 8.6289, 8.9829, 8.121, 8.622, 9.6019, 9.1698, 10.9777, 12.5685, 10.668, 11.6892, 12.8391, 11.5403, 10.8914, 14.4972, 11.3568, 16.9824, 16.2108, 15.8095, 16.2439, 19.4496, 16.0743, 16.4399, 17.9847, 19.2057, 18.8197, 21.3053, 22.0374, 21.3849, 25.7956, 22.1009, 23.0843, 26.3857, 26.344, 28.1266, 27.427, 27.6713, 29.1287, 29.9568, 32.6639, 32.0593, 34.8324, 35.4912, 37.0961, 37.8193, 38.5842, 39.5079, 38.5291, 39.8674, 38.4757, 42.6235, 42.1438, 44.7286, 45.8922, 44.0499, 45.5361, 47.5131, 48.2419, 50.2505, 49.2485, 52.0336, 50.4104, 50.6893, 55.974, 53.9157, 56.0057, 55.6881, 56.6658, 59.1415, 60.0807, 63.1207, 63.2666, 63.1401, 65.6013, 67.0657, 66.9267, 66.6113, 69.6515, 71.6706, 70.8938, 73.4347, 73.0344, 73.7733, 71.7095, 72.5082, 74.9264, 73.3068, 75.7195, 78.6953, 77.1688, 79.7252, 77.6437, 79.6611, 82.6532, 82.1326, 82.9418, 85.7346, 85.778, 83.1567, 87.0716, 84.778, 86.2636, 86.8847, 89.0257, 86.2587, 89.1488, 89.7572, 90.67, 89.5999, 91.8644, 89.7165, 92.9096, 92.1029, 89.8402, 92.898, 92.8808, 92.3838, 92.0979, 93.1064, 91.5472, 96.4694, 92.4429, 93.3244, 96.1936, 95.4926, 95.8129, 97.1038, 94.4354, 93.3874, 96.2557, 96.1944, 94.499, 93.0588, 96.7624, 95.6925, 92.6723, 95.7849, 92.3148, 93.5924, 92.4504, 92.9766, 92.4604, 92.5467, 94.2886, 93.3591, 93.555, 93.9247, 91.3813, 91.6944, 91.9962, 93.344, 91.7079, 91.9878, 90.9812, 90.557, 85.794, 89.2739, 87.7355, 86.9432, 85.4991, 85.063, 85.7191, 85.732, 81.5861, 83.5046, 81.6653, 80.3936, 81.707, 79.9085, 78.682, 77.7717, 78.5885, 77.3792, 74.9715, 74.5494, 74.7269, 74.7862, 71.7702, 73.2322, 70.4085, 74.8703, 68.4915, 67.8326, 68.4146, 65.8672, 67.5512, 68.093, 63.8833, 63.7909, 61.8157, 61.6468, 62.8435, 61.0812, 57.4227, 60.133, 58.5886, 56.6032, 53.9102, 54.0206, 51.7499, 52.6643, 50.507, 46.6476, 50.4076, 48.3243, 48.6746, 45.8737, 45.3837, 45.7993, 43.7361, 42.6178, 46.0425, 40.0067, 39.5579, 40.9542, 36.2002, 34.9067, 33.3871, 35.4096, 34.6287, 33.8915, 29.8461, 32.491, 31.8929, 29.0753, 28.4044, 28.5134, 27.2059, 26.7845, 23.3702, 23.9271, 21.5724, 23.5527, 22.6288, 20.9263, 20.7746, 17.2637, 20.1688, 17.4793, 17.6872, 18.4956, 15.0325, 17.3239, 15.0913, 14.3414, 14.5441, 11.2676, 13.475, 12.1396, 13.0834, 10.7036, 10.0023, 10.639, 9.5517, 7.4552, 8.52, 10.3269, 7.92, 6.4081, 8.462, 4.3026, 3.6174, 6.5271, 6.561, 6.3973, 4.5255, 5.4579, 3.4256, 5.7143, 2.8445, 4.9474, 5.4887, 2.4079, 4.227, 3.1822, 4.3244, 0.01, 2.5184, 0.1613, 0.01, 4.4283, 3.0488, 0.8545, 2.4054, 3.0577, 2.3866, 5.5072, 3.0685, 0.01, 1.777, 1.1364, 1.9657, 2.3247, 1.362, 0.7474, 4.4576, 3.348, 1.487, 3.5554, 3.5931, 4.5274, 1.0912, 3.8932, 1.8625, 4.5592, 2.5918, 0.01, 3.0457, 0.3746, 2.7848, 3.0843, 2.632, 1.0673, 1.689, 2.162, 3.1241, 5.4977, 4.6984, 2.9974, 5.6885, 1.7987, 4.9524, 3.7371, 4.4305, 5.1107, 3.2115, 5.5113, 4.375, 8.087, 4.0939, 6.5357, 5.6849, 3.036, 6.9216, 3.3741, 1.7013, 6.4142, 6.8231, 5.3834, 5.0, 7.8534, 6.6323, 4.6676, 6.8357, 3.9606, 5.0743, 4.4233, 2.7872, 4.4085, 8.0951, 4.7019, 5.6104, 4.7806, 7.9069, 6.6964, 7.5165, 6.7072, 5.1942, 5.5744, 8.3752, 7.1167, 8.4474, 2.6242, 9.1149, 6.7898, 10.8703, 6.9243, 7.8116, 8.8376, 6.6103, 9.3091, 7.1073, 10.0829, 9.4121, 9.0378, 7.2789, 8.1192, 8.9536, 5.5211, 7.8098, 10.1095, 6.8638, 10.7469, 7.947, 9.434, 7.2859, 9.7907, 9.8136, 10.878, 11.2569, 11.1569, 9.6393, 7.5343, 11.4053, 7.5079, 8.8488, 10.6537, 10.2528, 12.0148, 9.6552, 7.1604, 9.3151, 10.0199, 9.2618, 11.9335, 9.4688, 7.7099, 10.5856, 8.1837, 12.473, 10.3444, 7.8485, 11.5849, 13.5301, 6.9787, 11.346, 10.7222, 10.2626, 10.515, 10.7369, 8.9567, 12.1079, 7.9605, 12.9141, 12.2573, 8.1742, 9.9311, 12.0529, 9.1687, 9.6418, 9.2601, 11.5914, 8.9033, 9.773, 10.5691, 9.4166, 8.3959, 7.6489, 7.3478, 7.8178, 6.9672, 8.4166, 9.9144, 9.5008, 8.1336, 7.7989, 7.7865, 7.1439, 7.3196, 10.1911, 6.9573, 9.0333, 8.022, 12.5314, 6.133, 9.056, 8.2017, 7.4807, 9.044, 8.7371, 6.8317, 9.0047, 8.8781, 10.1865, 7.2342, 10.2808, 7.7048, 4.9599, 8.6316, 7.6468, 6.3669, 9.0108, 6.8233, 8.9751, 7.244, 8.4112, 8.222, 7.8291, 5.4872, 8.3303, 6.8984, 9.7838, 7.4126, 7.7352, 5.3748, 5.8263, 6.4937, 4.5059, 4.4191, 5.9244, 5.8832, 6.2159, 7.5012, 5.1379, 6.6727, 5.3476, 5.1869, 5.7771, 2.2038, 5.3192, 4.1769, 3.6545, 2.1342, 4.2163, 1.981, 6.0194, 4.273, 2.0271, 3.459, 5.5214, 3.4172, 3.809, 3.5992, 3.8836, 1.4503, 3.8567, 3.0137, 8.2084, 3.3862, 2.9513, 5.0477, 3.2623, 3.8089, 2.0514, 5.6305, 2.2924, 3.7346, 4.8091, 2.5941, 2.9924, 3.8156, 3.9045, 1.1914, 3.0464, 1.3053, 1.5561, 1.6538, 4.4043, 4.9308, 6.4013, 2.3671, 1.1504, 0.9205, 4.75, 0.01, 1.7965, 1.3921, 4.8373, 0.6651, 3.8123, 4.1063, 1.8645, 2.7931, 0.01, 1.9285, 1.724, 0.01, 2.6468, 2.548, 0.01, 0.01, 0.877, 1.2062, 1.5414, 0.5666, 0.4968, 2.475, 1.3109, 1.874, 1.702, 2.7466, 1.8454, 3.3871, 2.2691, 3.4698, 4.0304, 0.01, 3.5953, 4.9155, 0.8132, 2.2567, 1.8029, 3.036, 3.1925, 1.448, 4.4619, 1.2509, 2.3729, 4.1618, 5.6043, 1.9544, 3.9477, 3.1566, 0.5068, 1.503, 1.1855, 2.2959, 2.4854, 3.2135, 5.1505, 3.9314, 7.3422, 3.0637, 4.6802, 4.1866, 5.8946, 3.1376, 5.0456, 6.2944, 2.8437, 3.8877, 4.8325, 3.5008, 2.918, 7.0586, 4.0445, 7.0704, 3.7876, 4.3213, 5.91, 4.4881, 3.5324, 3.8849, 5.4583, 4.1351, 5.3517, 1.9195, 4.5543, 5.2074, 4.7956, 4.161, 4.4131, 5.9313, 8.1433, 3.9024, 5.6729, 7.6401, 6.7633, 6.208, 5.7594, 6.542, 6.4569, 3.4149, 5.7067, 3.9414, 8.8873, 8.2692, 5.937, 8.4101, 10.9587, 6.4335, 9.3987, 8.0745, 7.2559, 6.0316, 6.9353, 7.2546, 4.4503, 8.7957, 7.5925, 8.8381, 7.5934, 12.7917, 5.8793, 7.0355, 9.5615, 12.1089, 7.1488, 11.4462, 7.3905, 10.7685, 9.0025, 12.2941, 11.6803, 8.0926, 13.5231, 7.4872, 8.7553, 11.9885, 8.2237, 8.4115, 9.6156, 10.1428, 10.0877, 8.4295, 9.8828, 10.5814, 11.9172, 10.1878, 7.8486, 9.8014, 8.8568, 11.7934, 7.3271, 10.7033, 8.2873, 11.8133, 11.0799, 10.2682, 10.7109, 9.4605, 10.9899, 9.9103, 11.0882, 10.0841, 6.2773, 9.2295, 12.2753, 12.175, 10.1483, 9.7957, 9.4734, 10.9116, 10.822, 8.4706, 10.9156, 11.7545, 8.4531, 9.5834, 10.5355, 10.899, 9.1569, 8.9658, 9.0322, 10.2734, 10.3049, 9.5961, 7.5412, 7.9131, 10.4621, 7.6325, 9.7592, 8.8324, 8.4225, 9.3167, 11.7403, 9.6066, 8.676, 6.8541, 9.7757, 10.6542, 9.8005, 10.5774, 9.4809, 6.7474, 8.57, 9.3585, 9.5852, 7.6954, 8.2702, 8.8431, 10.1292, 9.6571, 9.0446, 8.5943, 6.386, 6.1956, 7.6314, 9.2528, 8.204, 8.3724, 5.1974, 5.1756, 8.9656, 7.1509, 5.1488, 8.3585, 7.3378, 11.293, 8.6554, 7.2146, 6.3818, 6.8705, 6.2815, 4.1904, 7.6529, 6.5868, 1.9552, 5.0467, 4.6696, 7.2969, 5.6723, 7.8825, 3.1773, 3.8642, 6.9921, 4.3226, 3.6317, 4.3442, 3.9095, 5.7737, 6.5579, 2.4168, 4.2781, 4.6533, 4.6735, 5.7191, 4.5688, 3.4398, 2.563, 1.4159, 3.5194, 4.0216, 5.3075, 3.2841, 7.2633, 3.591, 2.9097, 4.1358, 2.0405, 3.7422, 0.2665, 4.3118, 3.334, 3.4874, 3.3428, 1.1403, 1.6447, 5.2855, 3.6224, 1.9307, 3.4058, 3.335, 5.6058, 4.292, 3.1971, 1.8621, 5.1686, 2.2996, 1.1067, 0.5444, 1.0452, 1.7584, 3.4201, 3.611, 3.9389, 2.4146, 3.407, 5.5253, 3.4801, 4.4991, 0.01, 0.9481, 0.9199, 0.01, 2.1343, 4.1039, 1.2056, 2.297, 0.7199, 4.2624, 5.4387, 3.2492, 6.8574, 2.2931, 5.7508, 4.6478, 7.7574, 6.8264, 2.7384, 2.4331, 4.7876, 5.78, 6.5142, 7.1281, 8.3985, 5.6648, 5.3588, 8.8041, 8.2695, 9.0611, 8.678, 9.8247, 12.7771, 8.594, 9.6907, 13.0098, 14.935, 12.8297, 11.8159, 11.2974, 15.296, 13.654, 19.4306, 20.3349, 17.8331, 18.9084, 18.705, 21.3111, 20.3248, 21.1696, 23.874, 23.545, 26.5875, 21.7338, 24.166, 24.4657, 27.9179, 26.7596, 29.259, 27.4678, 29.9555, 34.4277, 34.334, 29.9614, 34.2372, 35.8486, 34.7951, 34.8147, 38.4007, 38.5044, 40.1329, 40.0231, 44.7177, 40.8151, 42.4104, 44.8986, 46.1986, 44.0099, 49.0028, 45.8499, 49.0279, 49.5395, 49.9202, 53.6627, 49.4226, 52.8258, 53.5449, 57.186, 59.1712, 58.9774, 60.872, 56.403, 60.9413, 59.5723, 62.0299, 63.8988, 64.6067, 67.4545, 63.3418, 65.9671, 65.9027, 66.5596, 71.4117, 71.0523, 73.9794, 74.2778, 73.447, 72.7241, 76.4883, 74.7735, 74.8098, 75.6686, 78.3493, 80.8237, 79.7495, 82.2382, 81.2551, 81.1801, 86.5881, 85.909, 84.1282, 83.2237, 82.6813, 86.3029, 85.9067, 87.5623, 87.9007, 88.607, 90.1844, 89.4425, 88.3675, 88.7597, 91.234, 89.1761, 92.2665, 92.6459, 91.8745, 91.114, 89.9473, 91.2313, 90.7633, 93.5067, 93.047, 92.5523, 95.4442, 94.5103, 94.5476, 95.4522, 96.2872, 92.4245, 95.9637, 92.2438, 93.7977, 96.5182, 96.0261, 95.9543, 95.1768, 96.9482, 92.4213, 94.1573, 94.5473, 97.3693, 95.6752, 95.491, 92.5684, 96.8412, 93.91, 91.4084, 96.1299, 90.1458, 90.6373, 91.2236, 89.7184, 91.5747, 90.7744, 90.0857, 92.2934, 86.5202, 90.5206, 86.7156, 89.6094, 89.7197, 89.7423, 85.4151, 83.3499, 84.9639, 83.7527, 80.5332, 83.6097, 80.4358, 83.2811, 81.8493, 79.2486, 78.9797, 77.8027, 76.7082, 81.2564, 74.4066, 75.0837, 75.6657, 72.7683, 71.5572, 69.6508, 72.5667, 70.971, 69.7227, 65.8604, 67.9761, 66.7377, 64.8838, 62.3997, 63.4467, 61.286, 61.7669, 60.7909, 59.2325, 60.1021, 57.7151, 57.6392, 56.5013, 56.4113, 52.4759, 52.4252, 53.3459, 51.212, 51.6887, 50.1362, 47.3701, 48.3776, 45.6918, 46.5359, 46.907, 44.7918, 45.6201, 38.8936, 39.4377, 38.9046, 37.7527, 37.9664, 35.9161, 37.1944, 36.0862, 34.3105, 31.2609, 32.6975, 31.9936, 30.6278, 29.2663, 30.456, 26.6774, 26.1052, 26.8221, 25.7955, 25.468, 22.965, 20.6337, 19.9771, 20.4255, 21.5181, 20.7868, 16.158, 18.981, 17.3628, 15.2594, 19.4534, 15.9239, 16.1463, 18.1504, 12.8383, 12.543, 14.8174, 10.0859, 11.6399, 11.0982, 12.0026, 10.6926, 8.271, 10.6745, 10.0787, 6.8489, 7.0279, 6.9251, 5.6641, 8.6797, 6.312, 6.1166, 4.733, 5.4749, 8.0921, 6.8061, 3.0976, 3.9004, 6.2209, 4.4241, 4.2665, 3.7521, 3.0141, 5.1429, 4.4403, 6.7534, 4.9963, 3.7235, 6.3082, 7.4042, 4.4201, 3.1298, 5.1462, 4.0418, 8.752, 4.4517, 6.3062, 9.293, 8.695, 6.9433, 6.885, 8.1532, 8.4508, 11.4607, 9.4087, 8.0596, 12.026, 12.5837, 11.1888, 13.2056, 12.6607, 11.328, 13.5884, 12.5821, 13.954, 14.1197, 16.6665, 17.1022, 15.5141, 16.8004, 18.8779, 20.295, 20.4378, 17.7644, 23.7638, 25.2642, 25.7668, 25.0052, 26.0073, 24.8402, 26.9381, 25.8041, 28.5381, 27.0768, 30.9748, 30.4503, 33.027, 33.0531, 33.0009, 35.8741, 33.7751, 36.071, 36.6014, 39.4691, 39.1909, 40.6652, 42.0091, 38.6667, 41.765, 44.3002, 46.6197, 46.9369, 49.023, 47.7047, 49.1694, 49.2503, 50.3282, 50.7172, 52.1282, 53.6363, 53.9217, 55.9135, 54.1176, 58.4561, 59.0452, 60.4091, 60.0006, 60.8845, 64.0658, 63.3, 67.4446, 63.2817, 65.9387, 65.5297, 66.9435, 67.8328, 69.1819, 71.953, 69.9287, 73.415, 71.1835, 73.5702, 76.9804, 76.504, 77.0174, 78.3394, 79.0305, 79.8675, 81.7761, 78.8439, 81.012, 82.7757, 83.6308, 84.0146, 85.7926, 83.7205, 83.0702, 87.6095, 86.2888, 86.0388, 87.4698, 87.3042, 89.9803, 90.3064, 89.9203, 90.7268, 90.4082, 92.3236, 87.644, 89.8223, 91.8805, 91.666, 91.1861, 93.8472, 91.7347, 93.7927, 93.8968, 94.0199, 92.8916, 94.0266, 96.3167, 95.4303, 96.1768, 94.7086, 92.3502, 94.2575, 94.7633, 96.9523, 96.6932, 95.5944, 93.6189, 95.0214, 94.3399, 94.2498, 92.626, 95.0122, 94.0906, 95.1399, 91.518, 91.7762, 98.6717, 91.5972, 93.0432, 92.9428, 90.4671, 94.166, 91.0906, 91.0081, 93.0869, 88.729, 88.7212, 88.4529, 86.4121, 88.3664, 88.1257, 87.1368, 87.2738, 86.9994, 85.6029, 84.1098, 85.6634, 82.1325, 84.6009, 81.5304, 82.2653, 83.1414, 76.2734, 78.6371, 77.5606, 79.9553, 75.9987, 79.9045, 72.7088, 74.7027, 72.7653, 73.2576, 70.7968, 70.1021, 67.1687, 69.1801, 65.8767, 65.2563, 66.44, 65.2089, 66.0054, 61.5021, 63.2238, 62.1306, 60.2423, 58.5343, 58.6604, 59.1401, 56.2849, 57.1446, 54.4883, 56.5092, 52.9833, 51.6996, 48.5654, 47.8593, 49.5903, 47.696, 47.4788, 45.051, 44.8753, 43.1908, 43.5972, 41.8586, 44.0179, 39.2281, 40.9823, 36.7714, 36.7097, 35.2059, 37.4247, 35.2646, 33.896, 33.1309, 30.2498, 33.0865, 32.1813, 29.1699, 28.3754, 28.4737, 26.3923, 25.5213, 24.2601, 23.6702, 22.677, 23.7005, 21.341, 20.432, 19.3892, 17.3492, 21.2222, 18.9431, 18.7934, 16.0277, 18.353, 15.7458, 17.0842, 14.279, 13.643, 12.3462, 12.7001, 12.3849, 13.768, 13.3384, 10.6822, 11.9131, 5.5642, 8.5107, 9.1856, 9.7561, 8.6943, 7.6298, 6.6959, 5.8831, 9.6507, 8.2167, 8.2513, 6.0826, 5.063, 6.5157, 5.4868, 5.1929, 5.2302, 4.2218, 3.7892, 4.6792, 2.9326, 5.8974, 6.2685, 3.8885, 2.3552, 5.7616, 5.6251, 5.4157, 3.5221, 2.807, 8.432, 5.7421, 7.0462, 6.4272, 7.4181, 5.1653, 6.7196, 5.6808, 6.4289, 7.3619, 6.1012, 8.5244, 8.5744, 8.3052, 10.348, 11.0118, 10.2735, 15.0653, 13.6612, 14.8206, 13.2174, 15.6395, 16.0536, 14.0002, 15.1439, 21.1954, 16.8768, 18.9105, 18.56, 20.8455, 18.8376, 19.5666, 23.535, 22.9035, 19.6746, 24.2242, 24.7264, 26.8875, 27.1368, 28.0133, 28.4867, 29.657, 28.3681, 29.7598, 30.5979, 30.7675, 34.8727, 36.2255, 36.4562, 35.5664, 36.602, 37.6933, 40.9735, 41.0502, 43.8293, 40.7315, 42.816, 42.2353, 42.9751, 45.8451, 46.5533, 47.2679, 46.7162, 50.2952, 50.5559, 53.3352, 53.7689, 51.9544, 55.4925, 54.9754, 59.1443, 57.7698, 58.0238, 59.13, 58.4445, 63.5585, 65.7713, 60.9371, 64.0871, 65.2253, 65.4183, 69.0471, 69.9948, 68.6498, 73.0099, 70.8278, 71.2816, 72.9197, 72.2257, 76.3582, 75.9256, 75.2785, 76.3307, 76.0326, 78.5603, 76.985, 79.6753, 79.6258, 81.9687, 83.3837, 83.6868, 82.9936, 82.7684, 84.0225, 85.0699, 86.4328, 85.4377, 85.4109, 87.6763, 89.9908, 91.2795, 90.1609, 87.9673, 92.0284, 89.0908, 89.628, 91.8304, 89.5041, 93.1429, 92.2057, 93.2105, 91.6821, 94.4719, 94.2363, 93.1654, 95.3512, 90.1449, 94.8598, 94.315, 93.4225, 94.9983, 95.5785, 93.7982, 94.7779, 94.9272, 95.5893, 96.8295, 94.2612, 96.879, 92.8724, 95.1837, 92.0356, 94.5839, 95.3997, 97.1698, 94.0074, 92.5879, 95.0064, 93.264, 93.0648, 93.9874, 93.9253, 89.3422, 91.6519, 91.1644, 89.1795, 87.9234, 89.8152, 88.7571, 87.8841, 88.265, 87.4261, 87.4425, 89.227, 86.2448, 88.5215, 86.9609, 85.9234, 82.74, 82.7373, 81.1268, 82.4436, 81.0175, 81.0641, 80.1243, 79.1772, 77.6312, 75.9348, 75.9464, 76.8859, 74.7548, 74.0884, 74.6465, 73.2314, 69.4323, 70.6581, 68.596, 68.9365, 68.709, 67.9849, 68.8054, 64.6397, 64.0224, 64.3928, 64.0353, 60.0997, 61.7259, 60.738, 58.5535, 57.3925, 57.9445, 56.5144, 55.5001, 52.8243, 50.3597, 51.8219, 50.575, 50.6872, 47.8716, 48.9892, 47.6713, 44.9678, 46.5185, 43.6119, 42.1097, 42.5621, 41.1989, 38.9026, 40.2343, 38.3394, 36.2704, 33.8373, 36.1749, 34.7576, 33.8694, 34.8445, 28.1405, 32.886, 32.1427, 27.7512, 29.8775, 26.2821, 25.0914, 25.6694, 27.7408, 24.7316, 23.4658, 23.9343, 20.8997, 23.3208, 22.6854, 20.2423, 17.6995, 19.7882, 17.9357, 17.788, 17.8721, 16.1827, 13.5514, 16.5346, 14.1289, 13.6244, 13.5065, 12.515, 12.6509, 11.325, 10.2809, 9.2592, 8.8737, 8.7839, 10.2951, 7.9201, 7.893, 7.5622, 8.1889, 8.0562, 9.7594, 4.4437, 5.9477, 6.9262, 3.337, 8.2957, 5.3496, 4.194, 6.7236, 1.8742, 5.4147, 3.5556, 4.9883, 5.5611, 6.1995, 5.6598, 8.2259, 5.2642, 4.7546, 7.2242, 5.4258, 6.311, 0.01, 6.7465, 6.1775, 5.881, 5.8132, 8.4375, 4.3256, 12.4376, 9.7908, 8.9801, 11.6615, 9.6281, 8.2983, 11.6251, 8.0932, 11.929, 13.3261, 12.8498, 10.4071, 13.4458, 13.5254, 13.1858, 11.4372, 13.7498, 15.4161, 18.5109, 21.6234, 19.4654, 17.6995, 18.9669, 21.9527, 21.9952, 22.4198, 22.9629, 25.19, 27.543, 25.0137, 26.0597, 27.4118, 26.8068, 25.0666, 29.9619, 28.9966, 32.9571, 32.15, 35.0254, 32.4039, 36.3059, 33.0878, 37.8747, 37.0518, 39.7593, 38.767, 41.2839, 41.9749, 42.7606, 43.8185, 46.647, 44.259, 48.1831, 46.5257, 49.8476, 52.5279, 48.8025, 47.7476, 52.2085, 51.9056, 53.1323, 54.4051, 55.073, 58.9921, 59.5023, 60.5177, 63.208, 59.5397, 57.9017, 65.6276, 63.9075, 64.3813, 62.6865, 64.2822, 68.2586, 67.9089, 65.7885, 68.4923, 72.3524, 73.5625, 72.8212, 77.5958, 75.7186, 77.7161, 77.8109, 77.8558, 75.3855, 75.766, 77.8134, 80.0581, 80.4032, 81.6801, 81.7898, 83.223, 84.6588, 83.8447, 84.04, 81.1629, 87.0092, 85.13, 85.4981, 88.7177, 89.3303, 89.491, 90.4007, 87.8116, 89.5717, 89.5343, 93.3786, 92.4847, 91.2087, 93.2025, 92.8482, 92.4357, 93.6176, 94.4781, 93.2534, 91.8347, 94.4652, 93.7392, 92.5983, 93.3239, 94.2408, 94.7733, 95.6713, 94.5849, 95.632, 94.4292, 95.8185, 95.5939, 95.8109, 97.0594, 94.4045, 92.3426, 94.4982, 95.6904, 94.3538, 94.6125, 91.3493, 92.4592, 94.2747, 94.3689, 91.7916, 93.1624, 91.2197, 90.2117, 91.6811, 91.5146, 91.3784, 91.4543, 92.4312, 90.8758, 88.4299, 89.8806, 85.7513, 87.5672, 88.4421, 87.3088, 86.9433, 87.9599, 83.3609, 85.025, 83.0375, 84.1784, 81.9962, 82.2912, 82.7217, 80.6169, 78.6107, 77.615, 76.3532, 78.1661, 77.7794, 74.6342, 71.4644, 72.3945, 73.151, 70.2243, 67.8409, 69.2364, 65.9575, 68.6118, 66.6191, 66.4001, 67.3359, 63.1029, 60.3099, 62.5498, 62.3503, 58.5516, 59.1871, 58.2583, 57.8559, 55.9223, 59.8689, 54.5053, 54.2018, 52.216, 47.9982, 53.053, 45.2648, 51.0536, 46.055, 45.0185, 45.9146, 48.3948, 44.6057, 42.8676, 41.0342, 42.197, 39.7051, 40.3748, 35.4079, 38.2004, 36.9452, 33.4949, 33.0601, 33.4692, 32.0495, 32.3114, 28.8302, 28.7702, 32.3923, 27.0919, 26.8608, 26.3896, 25.4038, 26.8097, 23.3308, 24.6735, 21.3226, 22.9931, 20.7534, 20.0673, 19.4655, 19.7073, 16.39, 16.0379, 19.8784, 16.9269, 14.9867, 15.8683, 14.2089, 10.3329, 13.3802, 12.4011, 12.5711, 10.3669, 10.6982, 12.2605, 9.8582, 8.9973, 9.7637, 8.2197, 10.0272, 7.7979, 8.2589, 6.332, 5.211, 7.8767, 6.9263, 7.2458, 7.1717, 3.6608, 5.8633, 2.515, 6.164, 3.639, 5.8722, 6.1853, 4.2336, 4.9761, 6.8335, 2.5299, 6.0083, 5.2739, 6.5935, 2.7896, 5.3903, 7.18, 7.7224, 4.7108, 5.6474, 9.0517, 4.4036, 6.7291, 7.3489, 7.6505, 6.3125, 9.5064, 9.7582, 11.5352, 8.0267, 8.9182, 11.7294, 13.2703, 11.0139, 9.2369, 11.7233, 14.0309, 14.1553, 15.3794, 17.1865, 14.2582, 14.1713, 14.3631, 16.6383, 19.0222, 20.771, 19.2101, 19.6205, 20.1301, 22.9141, 20.6904, 22.4483, 23.275, 24.1483, 27.7117, 24.4836, 30.3381, 26.517, 27.1248, 28.23, 31.0701, 30.9611, 33.5582, 31.5437, 31.1805, 34.0527, 35.9549, 34.2636, 37.2479, 39.1565, 38.0941, 39.0242, 39.5782, 41.9707, 43.8389, 48.2477, 46.8488, 44.1854, 44.7517, 47.8717, 51.3763, 50.9857, 50.1907, 53.4653, 55.0092, 56.1146, 53.2994, 56.5298, 54.9238, 56.606, 56.8612, 59.3469, 58.5418, 62.8092, 62.5738, 62.2351, 65.1929, 67.687, 66.8278, 66.3453, 67.6978, 68.8616, 68.8507, 68.2111, 71.0767, 74.3641, 72.1039, 71.283, 74.8675, 77.4652, 74.0253, 77.6793, 78.7837, 79.7307, 80.5062, 81.3138, 78.7422, 82.7439, 84.5726, 82.845, 84.3899, 82.9504, 87.0493, 84.7733, 88.7642, 86.2919, 88.4328, 87.1525, 91.2877, 86.0129, 90.0067, 86.7963, 88.5521, 93.6218, 92.551, 88.5588, 91.9313, 91.3567, 93.4666, 95.6929, 94.1831, 94.1186, 94.4725, 92.2946, 95.187, 95.698, 93.5577, 93.6513, 96.9075, 95.1424, 93.1977, 94.321, 94.303, 94.9032, 94.8802, 94.5833, 97.4146, 96.0772, 96.957, 92.073, 94.643, 96.0785, 95.2235, 92.9143, 92.5764, 94.9067, 92.8835, 93.8897, 92.8468, 92.1097, 92.1211, 90.3302, 93.7408, 91.6061, 92.4583, 89.1217, 88.6419, 88.4923, 87.1786, 88.9698, 88.4849, 86.0921, 85.8105, 86.2658, 83.9659, 83.858, 86.1735, 82.0576, 81.7311, 85.5769, 77.9285, 78.625, 82.3665, 78.3111, 78.9163, 80.5738, 75.5334, 74.9237, 75.6168, 74.0647, 72.5574, 72.3, 69.6523, 71.4773, 71.1547, 68.7756, 67.7879, 68.0439, 65.5024, 64.9287, 64.3623, 65.7398, 62.5185, 61.4763, 60.4496, 59.1641, 57.3317, 56.4355, 58.1361, 55.02, 55.0375, 53.2076, 50.9513, 51.2657, 53.0771, 49.3781, 48.0038, 47.3953, 46.5549, 46.4208, 44.4835, 43.9799, 42.4181, 42.7221, 41.6878, 40.5777, 40.2538, 38.2043, 39.2271, 37.1675, 37.767, 34.4895, 34.0985, 31.756, 31.8998, 32.2092, 29.5488, 31.4596, 28.394, 29.8748, 25.082, 25.4062, 24.2548, 25.705, 24.3542, 22.4911, 21.1497, 21.0343, 21.4163, 20.8521, 18.7925, 17.5096, 17.6467, 17.0164, 16.546, 14.8478, 17.5159, 13.3559, 15.9787, 15.2287, 14.0045, 14.8585, 12.3627, 10.5538, 9.7627, 9.0779, 10.8967, 6.9952, 6.6775, 10.7241, 9.579, 8.3353, 8.7732, 8.2442, 6.5175, 4.3304, 8.2142, 6.736, 3.8003, 5.8971, 4.8206, 5.2119, 4.7755, 5.9981, 4.7837, 3.3104, 6.0202, 2.7287, 0.01, 2.6175, 3.34, 1.7865, 2.3053, 0.8877, 0.01, 2.8435, 1.945, 2.7392, 1.7625, 1.7458, 2.4032, 2.0902, 3.5117, 1.6054, 0.1054, 2.5207, 0.3839, 0.01, 0.7673, 1.7941, 1.2992, 0.2309, 2.2434, 4.4511, 3.9182, 2.7357, 4.1559, 2.166, 1.5575, 3.9535, 1.3673, 3.5519, 3.072, 2.6275, 2.5469, 5.0035, 0.9786, 3.8323, 3.0635, 2.4528, 3.6932, 4.1794, 3.6878, 4.2184, 6.0472, 3.0987, 4.703, 1.5451, 4.9163, 1.5091, 5.3361, 3.7565, 4.7964, 2.4487, 4.7503, 6.0332, 5.8105, 4.9388, 5.0395, 6.8524, 6.3236, 4.3683, 4.5218, 4.4581, 5.5111, 3.9104, 2.5008, 5.4981, 6.2921, 6.2796, 7.7147, 4.2357, 5.4919, 4.185, 6.8042, 6.3652, 5.8873, 6.7568, 5.72, 5.3327, 9.3215, 8.2508, 6.8431, 4.8907, 6.9643, 8.2295, 7.7046, 9.0444, 9.4513, 7.5637, 7.067, 7.1054, 7.3037, 9.3568, 7.4389, 5.628, 8.9197, 8.1459, 7.6645, 5.4898, 10.2588, 9.6157, 6.9321, 8.93, 7.1405, 6.7875, 5.7414, 10.1181, 10.3272, 8.5994, 9.0298, 11.8058, 8.0927, 7.4995, 9.5172, 10.1978, 8.9743, 7.325, 8.3561, 10.5756, 10.6596, 7.1192, 7.8273, 8.2178, 9.6899, 13.069, 9.359, 9.908, 8.6445, 13.7417, 8.9825, 9.1376, 10.1575, 7.9527, 11.1666, 9.9533, 8.7121, 9.0911, 7.1504, 9.1629, 6.5871, 9.3243, 9.2557, 9.1127, 9.1172, 8.7218, 11.8273, 12.1246, 9.6991, 11.6547, 9.4634, 9.4515, 8.6422, 10.9099, 10.7673, 7.8792, 10.0277, 8.9832, 8.3751, 9.657, 6.783, 8.9743, 12.4218, 10.2016, 7.8035, 10.2942, 9.5526, 8.8923, 8.4472, 9.2776, 8.3372, 8.9136, 10.2288, 10.2877, 7.0627, 8.2465, 11.2998, 11.5232, 9.3207, 11.1167, 10.4497, 8.3732, 5.5093, 8.3433, 6.442, 8.4989, 10.8416, 8.5223, 6.8973, 6.246, 4.9795, 4.988, 10.2093, 8.9744, 7.4144, 7.3013, 7.3718, 7.3505, 9.3739, 7.788, 4.1457, 8.6263, 4.056, 6.2565, 4.7013, 6.7551, 4.7228, 4.4554, 4.8657, 7.5693, 5.983, 7.1753, 8.1277, 6.3575, 6.7679, 4.3713, 5.6115, 3.9281, 6.3953, 6.8372, 6.8403, 5.2631, 6.5628, 4.9162, 4.207, 5.7178, 5.9696, 8.1027, 5.8079, 2.9805, 3.0831, 2.5167, 4.5051, 2.66, 6.2743, 3.7157, 5.0387, 4.2275, 4.4406, 2.296, 3.7275, 3.0646, 3.4828, 3.752, 4.5185, 2.7305, 4.598, 3.7915, 5.6921, 0.01, 2.1465, 4.9388, 0.01, 5.1628, 3.0038, 0.01, 3.8626, 1.1445, 1.3049, 4.8234, 2.0, 1.0969, 5.505, 3.8456, 1.9107, 1.1905, 2.0328, 4.6704, 0.7982, 0.3304, 4.1241, 1.7619, 2.3105, 2.9236, 1.5295, 0.01, 1.7586, 3.9223, 5.3052, 1.8574, 1.9808, 4.0634, 2.9021, 2.4469, 0.01, 1.3755, 2.3231, 0.01, 1.4595, 4.578, 1.638, 0.01, 0.01, 2.0438, 1.2103, 0.792, 2.4559, 2.5635, 2.4217, 2.7109, 0.3688, 1.2805, 1.7827, 2.2237, 3.8231, 1.2825, 1.2462, 4.683, 2.7308, 0.4388, 2.1139, 0.7722, 0.9687, 0.722, 1.5803, 5.9162, 4.3962, 5.0083, 4.6241, 2.8486, 3.7157, 3.3258, 1.6318, 1.554, 4.7131, 1.6013, 3.7977, 4.5431, 4.7639, 4.2596, 1.9766, 4.6614, 3.7175, 2.5337, 1.0929, 4.8298, 1.9022, 1.7485, 7.0109, 2.6672, 3.9203, 4.7396, 4.8196, 3.2682, 3.8182, 6.1732, 4.6311, 5.0995, 1.9757, 4.4449, 6.6776, 4.3841, 4.1372, 6.2137, 6.6364, 6.3261, 5.0632, 7.1244, 8.5307, 7.9294, 3.9166, 10.7429, 4.4471, 4.6832, 7.0651, 6.5531, 7.3224, 8.8332, 8.3465, 5.6951, 7.2274, 8.7196, 8.8651, 9.0548, 8.6759, 8.5926, 6.8407, 5.7132, 8.4027, 7.166, 9.7907, 6.6912, 7.5033, 7.5071, 9.6727, 9.4215, 8.3995, 8.8731, 10.9258, 5.9272, 9.7594, 10.3223, 10.5875, 10.0322, 7.7286, 8.1405, 10.0006, 8.1802, 8.822, 6.9155, 7.4371, 9.7966, 8.6869, 8.2354, 9.1379, 9.9162, 9.115, 9.8069, 9.7202, 9.248, 9.6442, 8.3646, 8.9756, 7.0032, 8.0381, 8.0343, 9.2419, 11.5592, 9.8718, 10.1309, 8.5095, 10.3699, 12.4971, 9.8349, 8.7391, 8.236, 10.3858, 8.5842, 11.0691, 8.4815, 7.5149, 10.7161, 8.0509, 10.894, 11.5324, 9.0766, 10.2659, 12.0275, 9.2259, 12.3029, 9.3925, 8.4571, 9.9865, 10.1526, 11.4893, 11.0884, 10.2823, 10.6798, 9.9326, 10.8134, 8.8094, 7.9261, 9.8197, 13.4978, 10.6312, 8.3235, 11.4167, 6.4799, 8.8479, 9.7695, 8.1097, 8.1729, 8.7524, 8.1354, 7.7132, 7.2021, 6.426, 7.2527, 8.4851, 10.0807, 7.5086, 6.6175, 10.7734, 6.5794, 8.3642, 6.7446, 6.9967, 8.5798, 8.6157, 9.7535, 8.7723, 7.9215, 8.5478, 5.9854, 9.2655, 8.1671, 8.0204, 8.1006, 4.8997, 5.3344, 7.1492, 7.0832, 7.6429, 6.8545, 6.3317, 8.4088, 6.9598, 7.4686, 8.0544, 8.0704, 6.5638, 7.0887, 6.3117, 7.4343, 9.1593, 4.084, 4.4354, 7.8534, 5.6707, 4.6606, 4.1784, 6.5404, 5.8898, 7.9763, 4.7439, 6.1495, 7.1951, 2.481, 2.9702, 4.8764, 3.5302, 3.6773, 3.2395, 3.9311, 2.72, 2.0804, 4.5027, 4.038, 5.246, 4.4886, 4.2707, 4.6943, 2.124, 2.6812, 3.7749, 4.7, 4.2038, 2.8536, 2.8311, 3.4257, 1.2581, 4.2087, 4.0389, 4.2201, 4.2644, 4.2202, 0.01, 2.9643, 0.4356, 1.1069, 2.2939, 4.2265, 2.9092, 2.7219, 5.0172, 2.666, 0.8474, 0.1504, 0.9029, 2.353, 0.01, 2.7937, 2.578, 1.0148, 2.2331, 2.8355, 1.0986, 3.1334, 0.9487, 2.8965, 4.195, 2.4869, 0.0907] \ No newline at end of file diff --git a/skills/cloud/agent-platform-alert-configuration/assets/mock_steady.json b/skills/cloud/agent-platform-alert-configuration/assets/mock_steady.json new file mode 100644 index 0000000000..1b4fbe3e4c --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/assets/mock_steady.json @@ -0,0 +1 @@ +[50.1774, 50.1621, 48.2776, 47.481, 49.2367, 51.7367, 51.6358, 52.3998, 49.9103, 53.9292, 48.3077, 50.8816, 49.5168, 52.9828, 50.0645, 47.9358, 50.4837, 47.756, 48.2555, 51.8527, 49.9594, 51.7112, 48.2527, 52.5936, 49.6494, 46.0853, 48.2086, 47.2051, 47.0724, 51.021, 50.7313, 51.1816, 48.5254, 51.0634, 47.0639, 49.0342, 49.6825, 48.8594, 50.2009, 49.9545, 50.9422, 50.607, 51.9106, 49.0395, 51.303, 50.555, 50.4862, 50.2674, 45.687, 53.835, 48.1694, 48.4071, 49.3554, 47.38, 53.4918, 48.7606, 48.6324, 48.3406, 50.8741, 52.7917, 50.5455, 48.4516, 52.6456, 50.5671, 48.5552, 49.856, 50.2627, 50.4742, 50.0032, 50.0478, 53.7256, 55.6674, 50.346, 48.1656, 47.8995, 51.6263, 46.7519, 49.6364, 50.7899, 50.3633, 49.1907, 53.6755, 51.5925, 49.0583, 51.5665, 49.6321, 48.9675, 49.1848, 52.365, 48.8819, 49.8574, 52.0401, 50.6082, 50.4808, 48.6694, 54.6617, 51.6366, 45.2629, 52.6084, 47.4279, 47.2559, 48.6041, 52.5157, 49.4552, 50.4212, 46.66, 52.151, 49.4655, 47.4214, 50.6496, 50.9797, 50.17, 52.5828, 51.1673, 53.0044, 49.8904, 50.2191, 51.7284, 52.3173, 49.3338, 53.7717, 50.2307, 50.0097, 50.8837, 48.2886, 46.708, 54.1115, 52.1285, 48.1521, 50.2742, 51.2471, 49.0241, 52.8662, 49.011, 51.9848, 48.7391, 51.0143, 53.0997, 50.9489, 49.0238, 50.4433, 51.4497, 49.1439, 49.2101, 53.524, 52.7939, 48.6735, 47.3634, 47.4332, 47.7704, 49.4321, 49.8486, 47.2325, 50.3092, 44.1936, 50.2797, 53.2156, 47.5698, 44.2406, 50.3331, 51.4013, 49.1832, 50.4575, 52.5821, 53.3787, 51.196, 47.528, 50.3388, 47.9994, 49.9689, 49.5172, 48.8581, 53.0623, 49.5447, 47.0381, 50.7591, 51.3722, 50.1021, 48.4503, 49.1352, 48.8224, 48.9906, 48.5964, 49.5579, 47.2884, 50.4935, 48.4353, 51.7542, 50.7625, 53.0115, 47.2714, 52.8224, 49.7669, 51.2378, 51.7795, 51.9224, 50.1232, 50.9091, 49.8166, 51.4553, 48.2165, 50.8091, 53.0788, 54.0129, 52.1362, 52.9227, 51.0472, 52.1076, 53.8901, 49.8915, 46.8245, 49.6594, 50.372, 48.3521, 47.6043, 49.014, 47.54, 51.8253, 53.4094, 51.3508, 47.2108, 50.4141, 48.2834, 50.5595, 48.3537, 51.9657, 51.4046, 49.3588, 51.0089, 50.1264, 53.4549, 46.1969, 50.8258, 52.486, 49.2664, 49.0018, 50.3546, 48.1605, 48.9268, 47.9516, 49.6449, 45.816, 48.7945, 51.1169, 49.2429, 49.1762, 51.7979, 53.1556, 48.5255, 52.4099, 46.499, 48.3083, 51.0716, 50.5154, 50.2382, 51.7297, 49.5946, 51.525, 49.022, 48.745, 46.2502, 51.3527, 48.2644, 52.0151, 52.0194, 49.5556, 50.1745, 50.3956, 49.5899, 47.0828, 52.8915, 48.8644, 51.0589, 50.5827, 50.5793, 50.4379, 51.8242, 48.2585, 51.9841, 50.3317, 50.0785, 49.1698, 49.9092, 51.1583, 47.6561, 46.5588, 49.4505, 51.4042, 49.4019, 52.7424, 52.5921, 52.805, 52.9046, 50.5271, 51.1339, 48.6358, 44.629, 50.8108, 48.4565, 49.5977, 45.0292, 51.7951, 49.5965, 48.8185, 47.6902, 48.3748, 48.3062, 48.0579, 47.1739, 49.5804, 49.3237, 51.7188, 46.497, 49.1084, 48.2173, 50.6818, 49.8952, 51.698, 53.6764, 50.0021, 51.06, 48.5007, 49.7463, 50.0497, 54.2777, 47.7209, 49.7319, 50.0389, 48.4913, 50.7049, 50.0283, 50.2391, 48.5556, 51.4692, 48.1391, 49.2557, 51.6947, 52.7958, 48.4995, 51.2457, 49.8293, 52.088, 50.2229, 53.3941, 53.0652, 48.0535, 49.3464, 48.499, 49.8384, 46.3655, 46.6526, 52.0396, 50.6815, 51.507, 48.1906, 47.8978, 49.6129, 52.7657, 50.1508, 50.366, 52.3556, 50.7593, 50.3639, 50.8548, 49.6186, 53.1468, 49.7913, 48.559, 51.3632, 51.8933, 49.0565, 51.448, 52.1531, 43.2966, 46.5506, 50.801, 48.1389, 53.3729, 50.6481, 49.7968, 50.1235, 49.2402, 48.7162, 50.2437, 52.0058, 46.9059, 50.4816, 49.1008, 50.1722, 48.4964, 50.2877, 47.8557, 49.9594, 49.4794, 46.6695, 50.8365, 44.3763, 49.2486, 47.0188, 50.514, 49.3279, 51.6339, 49.5958, 49.5741, 52.2412, 49.0704, 49.9406, 52.8525, 51.3238, 53.9658, 50.9181, 48.9407, 52.1481, 51.8447, 49.4538, 51.0802, 49.8352, 50.9961, 50.0652, 49.7402, 53.3775, 50.9124, 47.339, 50.4168, 49.4017, 50.626, 50.4799, 50.7325, 46.6032, 46.8642, 52.8012, 49.9467, 52.6762, 51.0552, 48.9338, 46.623, 51.0239, 51.0346, 53.5024, 49.595, 50.1523, 46.3839, 48.7863, 50.4919, 49.2984, 48.2521, 52.8355, 53.2054, 47.8637, 50.1978, 49.143, 52.9696, 50.4749, 51.1946, 48.1299, 52.655, 49.4021, 53.7258, 52.1849, 48.6981, 51.0538, 50.4868, 49.5574, 51.4997, 49.9491, 50.0563, 48.7369, 48.5112, 50.1532, 50.3574, 52.1517, 47.2909, 50.225, 53.1878, 48.6439, 47.9131, 48.1468, 51.5829, 47.4677, 50.039, 48.6616, 48.8491, 49.3874, 49.2509, 49.2649, 49.8921, 50.5379, 49.093, 50.9412, 49.0408, 49.0918, 50.0837, 46.5675, 48.3581, 48.664, 47.9544, 49.5369, 48.5335, 48.1776, 52.032, 54.1649, 51.9879, 50.5962, 52.248, 50.6284, 49.4786, 50.223, 49.3283, 45.5904, 48.3173, 48.342, 50.9672, 49.038, 46.4005, 49.9428, 50.5734, 51.5386, 50.7286, 54.7812, 48.2652, 52.6343, 51.842, 48.7292, 49.7438, 48.5077, 49.7522, 49.1139, 49.1103, 49.9895, 47.9284, 52.2491, 51.7562, 48.6094, 51.7254, 47.8972, 46.432, 50.6298, 51.0931, 48.5213, 51.0524, 53.921, 56.0248, 48.2541, 50.6533, 47.5429, 55.663, 49.1283, 51.72, 50.1889, 47.8859, 49.5863, 47.2927, 48.7201, 49.4638, 50.4499, 45.4565, 49.8688, 55.106, 48.0965, 50.6517, 49.1537, 44.3556, 48.0092, 48.633, 51.694, 49.9298, 49.5594, 51.3464, 50.2583, 49.6701, 51.3364, 47.2475, 49.5875, 52.5067, 45.7197, 48.4187, 50.3126, 50.4583, 49.3273, 50.8598, 48.2269, 51.9589, 50.3894, 53.6304, 51.6932, 51.1032, 52.8322, 48.3935, 49.4135, 46.4996, 51.741, 53.3083, 49.029, 47.5657, 52.2497, 48.5254, 48.9912, 53.079, 49.2825, 49.6271, 53.2237, 51.1818, 47.8719, 53.0106, 46.5173, 52.015, 50.9804, 48.9532, 48.4422, 47.7752, 53.458, 50.2489, 48.7388, 51.1478, 52.4092, 50.6377, 51.394, 51.908, 50.1175, 50.6332, 49.7692, 48.0153, 47.466, 50.9012, 48.5832, 49.9579, 49.5046, 51.9406, 50.5599, 50.6129, 49.427, 49.724, 49.3202, 50.4992, 46.5518, 49.9605, 50.7351, 50.7865, 47.845, 49.1578, 49.2385, 48.9201, 47.5663, 47.8271, 50.0182, 50.771, 50.296, 48.257, 50.77, 49.6024, 49.0089, 48.1494, 48.9813, 51.434, 49.6321, 49.7537, 47.9979, 55.5808, 45.4701, 45.9442, 48.9503, 50.3988, 53.5471, 51.0276, 50.6016, 51.8393, 50.1902, 46.0898, 50.5097, 50.4346, 54.3393, 48.5581, 52.5148, 47.2796, 53.0656, 50.2652, 51.674, 51.4076, 51.3556, 50.1951, 48.688, 49.6806, 48.4691, 49.7746, 46.7823, 50.3445, 51.9546, 52.4038, 52.6276, 50.305, 49.6917, 49.4895, 50.7847, 51.4342, 50.6763, 47.3092, 52.7561, 50.955, 51.9814, 50.1038, 49.478, 53.8751, 47.8603, 46.6237, 53.9916, 50.4808, 49.1433, 48.8299, 45.9878, 53.7345, 51.4113, 51.02, 51.6493, 48.1707, 50.9168, 52.1714, 45.5902, 51.1919, 51.4942, 50.203, 48.2289, 49.6689, 49.0836, 50.7257, 48.9358, 51.5288, 48.9633, 48.9603, 52.3371, 49.6785, 51.0632, 47.4052, 53.0418, 47.0957, 50.1939, 49.5509, 48.4739, 49.4512, 52.4702, 53.4779, 49.5267, 49.2851, 46.2441, 53.4302, 47.8679, 48.1802, 50.2096, 51.4681, 53.0465, 54.3492, 51.5077, 49.0397, 48.6864, 49.643, 51.6089, 51.9098, 51.2339, 46.2111, 48.8677, 52.9134, 48.9338, 49.8393, 55.4536, 49.8521, 52.8469, 53.6774, 51.5164, 49.1302, 45.4399, 48.2821, 51.2866, 51.1839, 52.3445, 47.7883, 50.3989, 45.8998, 48.2344, 51.0586, 45.6355, 51.6334, 53.6613, 47.1278, 49.4823, 53.4369, 51.0013, 50.7947, 50.215, 47.2564, 49.7263, 52.5733, 50.174, 51.4434, 49.2765, 48.2571, 49.1376, 47.8939, 47.2754, 49.9602, 49.5424, 48.1192, 51.3921, 48.1368, 50.8512, 50.9968, 53.3319, 50.6841, 51.5792, 50.1494, 47.1856, 51.7114, 53.3588, 49.7514, 54.1876, 51.8477, 54.002, 49.6376, 48.2307, 50.112, 48.5475, 50.3969, 49.5167, 50.329, 49.8199, 48.0515, 48.9412, 52.5383, 49.873, 47.3183, 45.9664, 49.3551, 50.5576, 47.6396, 46.4254, 51.1216, 56.3882, 51.744, 48.0068, 49.7647, 49.7623, 47.4564, 47.1917, 49.7145, 51.5803, 50.5058, 48.6334, 51.0188, 51.0425, 48.0346, 47.5643, 51.2904, 52.2724, 50.8108, 52.3585, 52.934, 46.9433, 50.9572, 52.6319, 52.2672, 52.8544, 48.8248, 51.3635, 51.5526, 48.8037, 50.3814, 47.3366, 48.3023, 50.6551, 52.1028, 49.9984, 53.0682, 52.9786, 48.0343, 49.7972, 48.9931, 51.6465, 51.4363, 48.4126, 51.8482, 47.3587, 52.3557, 45.1217, 49.6418, 48.5842, 51.3519, 50.5051, 45.6169, 47.3092, 48.8257, 52.3676, 53.2625, 51.364, 52.8459, 48.4938, 50.8332, 50.254, 47.8404, 49.2992, 49.2863, 50.9122, 50.9908, 48.9031, 48.9362, 46.1987, 51.0106, 49.7932, 49.942, 51.0916, 49.0093, 48.0723, 48.7959, 49.3127, 50.4353, 48.7883, 49.1185, 47.3408, 49.1848, 46.5059, 48.6785, 51.0799, 50.3428, 49.6876, 49.5509, 51.8006, 47.6431, 50.8845, 48.5671, 50.5565, 49.6958, 48.9135, 50.6246, 48.7076, 47.5741, 49.034, 47.4906, 50.2903, 48.7842, 47.4722, 50.7286, 50.8036, 49.6309, 49.4154, 51.8266, 49.4577, 50.5161, 47.73, 51.1152, 49.7141, 51.0365, 49.4538, 48.4941, 47.596, 49.5874, 49.0812, 47.4056, 52.5018, 52.0513, 45.9307, 45.722, 46.9608, 50.3131, 55.3594, 47.6363, 49.3655, 52.6658, 49.0093, 52.2318, 51.8683, 49.9416, 51.7641, 51.8718, 47.1942, 49.6508, 48.7418, 50.0593, 47.5726, 48.0071, 50.1116, 49.9854, 52.7712, 50.4208, 52.8647, 52.6207, 52.2757, 42.9303, 48.454, 50.9269, 47.3536, 48.9442, 51.3372, 50.0951, 49.3023, 53.4649, 49.1558, 52.2227, 49.9006, 50.1983, 49.2829, 48.3188, 49.5568, 47.2686, 51.2223, 50.2261, 49.1198, 51.5888, 47.9155, 46.0648, 50.8429, 47.2451, 47.2094, 50.3259, 51.9326, 48.4429, 48.2156, 53.0125, 47.175, 51.6959, 50.8597, 48.5485, 49.7272, 48.2801, 48.6678, 49.6944, 48.5862, 46.7048, 51.4247, 48.7914, 51.5932, 49.6921, 55.6256, 51.1162, 47.3073, 47.2557, 48.2072, 49.1428, 46.6948, 47.748, 50.0303, 46.8739, 49.8001, 50.466, 47.9902, 51.2079, 50.1701, 52.1867, 51.3294, 49.5747, 49.6849, 50.9236, 50.7555, 46.6144, 51.8531, 48.8781, 52.2681, 50.3583, 48.7008, 49.9996, 52.881, 48.5009, 46.5808, 49.4886, 52.5188, 53.1763, 52.0655, 45.7595, 49.5257, 49.0439, 49.6471, 47.8101, 49.4499, 47.6318, 54.6402, 50.1199, 48.965, 47.521, 51.9158, 51.167, 52.324, 50.6933, 49.063, 48.5442, 48.4713, 49.1898, 51.3706, 48.8968, 50.5685, 51.5059, 49.0919, 51.957, 50.8406, 50.7663, 49.9481, 50.3468, 48.2792, 48.3231, 50.5005, 51.7888, 49.539, 51.0043, 49.5382, 48.4344, 50.025, 50.7685, 48.6225, 48.812, 49.0097, 50.3661, 47.05, 50.5486, 50.5232, 50.4107, 48.8756, 50.5599, 47.7934, 48.4776, 51.5981, 47.3427, 48.711, 48.8445, 51.2992, 47.1198, 48.822, 47.8293, 53.3122, 49.4472, 48.9613, 50.2274, 51.728, 45.8541, 47.8381, 50.4367, 48.7404, 47.1152, 48.5821, 50.3132, 50.6514, 53.9201, 46.7216, 47.0379, 47.5844, 51.9458, 48.2081, 46.9622, 51.6247, 48.5198, 52.2306, 50.4293, 48.6763, 51.7877, 51.6318, 53.0039, 49.1727, 49.363, 52.9442, 52.2231, 50.5143, 48.3, 47.9055, 47.1379, 48.0185, 49.5865, 47.6164, 51.4413, 44.8429, 48.3975, 48.6396, 51.2174, 51.0453, 47.5385, 50.4575, 47.9112, 50.5829, 47.5604, 50.2562, 52.9371, 54.0804, 52.1734, 47.9368, 48.962, 48.1336, 48.7797, 46.5044, 48.7393, 52.2356, 54.4687, 50.8694, 50.4065, 51.346, 46.8389, 48.3085, 51.6104, 49.5339, 49.2638, 51.7084, 46.1865, 52.3345, 48.3758, 51.1267, 50.0013, 51.4569, 51.9847, 47.9761, 51.6747, 51.0129, 52.8509, 48.9438, 52.3751, 51.4573, 49.1158, 52.0023, 49.2554, 51.8264, 49.7993, 50.2162, 50.1359, 52.028, 49.8266, 51.1614, 51.1993, 48.2815, 51.848, 52.9459, 51.0216, 50.8115, 48.2964, 52.5786, 46.9323, 50.2883, 53.0997, 48.622, 51.0791, 48.5474, 51.317, 47.1699, 49.1755, 48.7244, 50.2031, 46.5976, 53.0151, 50.6201, 49.0223, 52.978, 49.9524, 50.513, 50.5766, 53.1816, 50.315, 47.8646, 50.1788, 50.8397, 49.9483, 48.7007, 48.3836, 46.6061, 50.9623, 48.331, 48.715, 51.019, 52.1584, 47.9288, 49.2974, 49.2121, 51.0117, 47.92, 48.3955, 50.8401, 48.7935, 47.9428, 49.9777, 48.1673, 49.453, 52.5676, 49.6466, 49.7258, 49.0589, 46.9575, 50.3546, 52.0338, 52.5252, 55.7068, 49.4091, 50.4035, 47.8, 53.704, 51.9831, 51.9215, 51.1933, 49.7346, 44.9243, 52.5001, 51.3355, 50.5153, 48.3471, 51.735, 50.5974, 50.4011, 47.7862, 47.756, 49.8211, 49.7877, 47.9199, 53.4692, 49.1228, 49.8692, 50.5202, 52.9443, 48.4497, 49.6286, 47.451, 52.0977, 50.8523, 50.317, 50.5688, 53.6319, 49.7799, 48.8322, 48.8422, 52.0619, 51.1763, 50.9604, 48.7031, 51.093, 51.2018, 50.2121, 49.7365, 50.947, 50.7991, 49.0402, 49.3758, 46.5696, 51.1949, 49.7805, 53.0991, 46.4313, 50.3392, 51.186, 48.6028, 50.966, 50.5664, 51.059, 49.5333, 43.6465, 50.1618, 50.1882, 48.5376, 47.3494, 51.0459, 50.7929, 53.1585, 48.387, 52.371, 50.0342, 49.0074, 50.9984, 51.753, 48.9496, 53.1408, 50.0376, 47.5339, 47.7227, 52.1439, 48.8941, 50.1781, 50.2119, 49.7751, 51.9587, 49.0362, 47.7806, 45.3972, 49.763, 49.4719, 52.4765, 48.5918, 52.0464, 49.4219, 53.4414, 48.0526, 49.1977, 51.3573, 52.3769, 51.6879, 47.3298, 52.1766, 49.9814, 46.7438, 48.8824, 49.3032, 48.4016, 52.6118, 51.0738, 52.2788, 48.2396, 46.6599, 50.9043, 50.0681, 51.7265, 51.3203, 50.3118, 51.6024, 50.1533, 50.4103, 53.9569, 52.5794, 47.8961, 46.8361, 52.7146, 49.1272, 49.7367, 49.0237, 50.6542, 49.1722, 47.6193, 46.43, 45.2162, 47.8916, 52.4494, 48.1246, 49.953, 52.243, 52.6278, 48.886, 51.4226, 49.249, 46.1232, 51.9149, 50.2203, 48.2459, 50.412, 50.6673, 50.333, 48.0636, 49.6506, 50.6111, 51.5076, 49.5682, 47.8336, 50.8487, 52.4739, 48.8089, 51.0117, 52.0965, 52.1167, 52.6893, 50.5667, 52.8154, 52.7046, 48.3452, 50.6201, 50.4704, 48.6309, 49.3416, 45.984, 51.4207, 51.6978, 52.3018, 52.6974, 48.7901, 49.68, 53.0872, 52.1593, 48.5249, 45.2792, 49.5423, 47.863, 50.1253, 51.9418, 47.1057, 50.5496, 47.5685, 54.9648, 50.1032, 50.9503, 47.9119, 45.7345, 48.0823, 48.0865, 52.3452, 47.7049, 51.654, 50.843, 49.7769, 45.0271, 48.9007, 51.9415, 51.0746, 50.2992, 47.1262, 50.2521, 50.5074, 49.0278, 50.8663, 48.5794, 45.9845, 48.7135, 48.6194, 52.438, 51.439, 49.8989, 48.9065, 51.1165, 52.9883, 48.0659, 48.7218, 52.3015, 50.9617, 49.1231, 52.3105, 51.1032, 48.1144, 50.5493, 49.9856, 53.3053, 51.2148, 50.967, 50.2666, 51.0419, 47.2217, 47.0912, 50.7787, 50.7994, 49.193, 46.0038, 47.8244, 51.0756, 50.2538, 48.7173, 48.142, 50.533, 49.6983, 49.6813, 48.9913, 50.3005, 50.5654, 48.7473, 53.3254, 48.8224, 50.3326, 46.8969, 48.0021, 51.8409, 51.4528, 50.4293, 48.6549, 50.7073, 48.0141, 52.1401, 44.7421, 49.23, 48.4055, 52.5897, 49.604, 52.4748, 52.8913, 50.9095, 46.724, 48.4923, 49.0017, 50.801, 50.7593, 47.3408, 51.5692, 47.9706, 49.2922, 50.5148, 49.7404, 53.1377, 52.2787, 47.3619, 47.7486, 52.2996, 50.2266, 51.2567, 50.0502, 52.4611, 51.5433, 51.5729, 48.6466, 51.2149, 46.7796, 49.5185, 48.7291, 48.4466, 48.1264, 45.9848, 52.4768, 49.5744, 49.6455, 51.0025, 52.1304, 52.6737, 49.1368, 50.376, 48.1893, 51.4656, 50.0421, 51.8182, 51.6546, 51.5634, 51.0723, 50.1741, 47.5146, 50.6581, 53.3669, 49.1322, 48.6453, 51.2361, 53.3486, 47.9179, 45.1683, 48.7703, 50.1994, 49.7144, 49.4794, 48.9947, 48.7063, 48.3456, 50.72, 51.145, 46.8293, 53.1638, 50.883, 51.1079, 49.4343, 50.5096, 47.7528, 53.1668, 52.8298, 49.5209, 47.3625, 49.3883, 51.1826, 49.2124, 48.4087, 50.3217, 48.2524, 52.6705, 51.891, 51.3276, 48.5842, 50.4137, 52.2188, 45.8138, 50.4114, 52.8124, 53.623, 48.0989, 50.5524, 52.0886, 52.4117, 46.3207, 45.9808, 50.3086, 50.9966, 49.7509, 49.3443, 52.5561, 52.955, 48.8156, 50.3154, 47.6862, 53.471, 47.1915, 47.8598, 48.2204, 47.903, 52.5551, 54.6433, 50.5214, 49.3279, 51.4435, 48.186, 52.6755, 49.9271, 50.8411, 48.6175, 49.5912, 46.7608, 50.0374, 51.7805, 46.2969, 47.9803, 49.0477, 49.3807, 49.8195, 49.9099, 50.5675, 49.929, 46.6362, 50.6274, 52.4068, 50.5161, 46.4531, 50.4646, 50.9507, 48.0215, 50.1524, 52.5529, 53.2348, 50.253, 51.0149, 49.6698, 49.1803, 52.77, 51.9249, 51.234, 48.9469, 52.9345, 52.0809, 47.4166, 52.1772, 50.3366, 50.557, 45.9659, 47.0737, 51.979, 48.466, 51.6249, 47.3457, 47.0495, 53.8215, 51.8188, 49.7433, 50.4474, 48.5828, 50.712, 52.3589, 49.1691, 50.9713, 46.6156, 53.6031, 52.0092, 49.94, 49.3311, 52.822, 48.9905, 50.7395, 49.3722, 48.1545, 52.0777, 49.1756, 50.9284, 49.3054, 49.7311, 49.6579, 48.6903, 51.4666, 49.8264, 51.7255, 51.4936, 52.4446, 49.017, 51.3274, 49.6512, 49.9552, 48.5711, 49.0511, 51.2603, 49.316, 48.5411, 51.6966, 50.7136, 47.563, 50.2114, 52.2564, 52.1924, 48.9257, 47.7786, 49.2689, 52.6882, 51.4875, 45.4126, 51.7175, 45.5643, 53.8651, 51.6907, 49.2108, 50.2139, 51.322, 49.0132, 50.9271, 49.7409, 49.4984, 53.05, 45.9225, 50.0941, 47.2138, 49.6358, 48.5838, 55.0926, 48.9716, 49.726, 48.7632, 51.7788, 48.9797, 48.69, 53.7408, 51.5619, 50.5379, 54.8248, 48.734, 48.3748, 48.413, 52.4063, 48.7624, 52.3805, 48.7412, 46.3707, 48.3968, 50.5743, 52.3846, 52.6027, 50.7328, 51.3682, 44.7111, 50.5253, 50.0771, 49.3821, 48.6682, 51.7595, 52.2785, 53.9292, 49.6105, 48.4079, 53.7705, 49.3592, 48.9588, 47.2256, 50.8402, 51.2114, 51.6872, 50.4462, 51.0882, 51.1086, 51.0288, 52.3569, 51.7049, 50.3776, 48.9255, 51.6059, 48.8457, 49.7047, 48.5676, 54.2659, 48.0577, 51.3278, 51.3526, 51.7524, 49.9208, 50.1678, 49.6064, 44.5639, 48.4715, 52.7281, 48.3675, 50.27, 55.2752, 48.0637, 48.9907, 49.4959, 46.0031, 48.2635, 47.1427, 51.0878, 49.5058, 45.735, 51.6203, 51.9442, 49.2766, 51.8934, 51.1747, 48.1713, 50.5625, 50.3694, 48.3297, 47.9467, 50.8158, 54.6134, 49.9254, 50.4206, 47.8911, 49.4809, 49.5953, 49.2465, 48.6884, 49.9027, 48.8741, 52.4829, 54.0998, 48.4773, 49.2728, 50.8672, 51.1662, 48.473, 48.8583, 51.1732, 46.7233, 54.4301, 50.6029, 49.3918, 49.6993, 47.8532, 53.605, 49.8322, 49.1298, 53.514, 52.0639, 49.6111, 50.8725, 49.3357, 49.2756, 49.5581, 50.5949, 52.1898, 53.9551, 45.6548, 54.6447, 52.0487, 50.0331, 49.1399, 46.2638, 52.3888, 47.8108, 51.3037, 49.1705, 46.7485, 50.8368, 50.9727, 48.1284, 51.7188, 53.6108, 50.9813, 50.8628, 48.5324, 47.9213, 49.1878, 50.138, 50.0235, 49.6022, 54.9835, 51.1423, 48.0474, 47.5988, 46.5229, 47.9809, 51.1574, 49.4295, 48.6143, 51.1793, 51.0984, 50.7428, 49.9735, 49.9265, 47.3628, 46.3096, 48.3872, 48.4226, 48.3621, 49.6536, 49.0519, 47.3908, 49.7942, 50.672, 49.0149, 51.6381, 50.6309, 50.7789, 49.2529, 50.8269, 51.365, 46.1415, 52.1366, 48.9777, 51.3012, 45.3873, 49.1317, 46.602, 49.7032, 50.3629, 50.8139, 50.0414, 47.4949, 52.6055, 47.3095, 49.3274, 48.4465, 48.7949, 47.1938, 52.1712, 51.4922, 50.1433, 49.7113, 48.0801, 49.7798, 48.1839, 50.1728, 50.7875, 49.5057, 50.798, 51.1672, 51.6093, 53.9495, 47.1446, 47.5323, 47.567, 49.6044, 46.8111, 48.9391, 51.6762, 51.4585, 53.2791, 51.7073, 52.026, 48.9421, 49.2839, 51.7577, 51.7172, 46.1673, 47.2228, 48.0443, 48.2801, 52.6347, 53.6118, 50.6599, 50.6814, 53.8839, 50.8974, 51.1181, 50.7454, 50.7486, 52.6982, 51.4527, 50.1707, 49.2137, 50.5201, 46.6746, 50.9215, 50.2522, 50.6416, 47.6865, 50.9374, 50.3662, 49.986, 50.7404, 48.3407, 47.0873, 50.9082, 45.5965, 51.1901, 54.1351, 50.7416, 51.7074, 53.2554, 52.0884, 54.554, 48.2577, 49.3176, 50.9667, 46.7702, 49.9871, 50.1335, 52.5958, 52.1345, 51.5325, 51.3935, 50.1228, 50.5844, 51.5189, 52.0234, 48.5619, 50.8992, 49.8493, 50.5311, 52.3088, 49.4987, 48.156, 48.178, 48.8537, 51.219, 49.6686, 48.8039, 51.5455, 50.3199, 51.4011, 54.386, 53.7625, 50.6897, 50.6778, 52.2273, 52.5558, 49.2837, 47.5499, 51.7259, 50.4502, 51.2204, 51.0858, 48.6031, 48.9799, 52.1426, 49.6769, 48.4369, 49.4493, 50.7953, 48.9796, 53.4748, 51.6247, 47.0196, 50.4591, 53.1379, 56.5926, 49.0584, 50.0495, 47.2873, 51.1125, 50.9577, 50.5664, 47.8315, 51.5625, 53.6563, 52.7174, 50.7245, 49.8985, 51.7966, 49.7178, 44.9419, 46.5643, 52.7364, 52.6606, 49.7492, 48.4035, 49.3994, 47.4278, 49.4921, 53.494, 48.7488, 48.4112, 47.3121, 50.4114, 48.1002, 51.3008, 49.3828, 53.3626, 51.1826, 50.954, 47.1864, 50.0482, 50.6076, 50.6857, 50.7108, 50.2451, 47.6646, 45.1864, 47.7677, 52.0279, 51.6007, 52.2847, 50.6562, 50.4541, 50.0346, 46.5835, 50.3563, 48.7048, 48.7818, 48.0235, 49.8656, 51.8303, 51.5995, 48.0584, 50.4845, 49.8512, 45.9776, 51.8803, 48.7061, 51.1607, 48.4177, 51.5559, 52.4968, 50.523, 51.0863, 48.693, 49.366, 47.7621, 52.1054, 52.3727, 49.0403, 50.0758, 48.6665, 47.3819, 48.9705, 47.6056, 46.119, 50.5919, 48.0086, 49.9163, 44.6843, 50.1774, 48.6158, 48.7493, 51.741, 49.7554, 50.5983, 50.1069, 50.9624, 48.4169, 49.9767, 49.5335, 51.4178, 48.8471, 51.5066, 51.0568, 48.8379, 51.4531, 49.2016, 48.7199, 48.3987, 47.851, 50.6225, 49.6071, 49.3861, 51.7082, 51.7633, 50.2195, 46.1293, 50.9703, 46.0824, 48.2148, 49.6783, 53.0694, 45.0365, 46.9373, 48.5692, 51.3824, 46.7358, 48.3999, 52.4256, 51.7636, 48.7842, 52.0276, 50.1703, 48.1863, 48.573, 48.7447, 47.8426, 50.9177, 52.0199, 47.1195, 51.3327, 47.911, 49.8548, 50.6071, 52.0277, 49.9519, 50.4975, 53.2586, 53.2305, 52.1063, 51.501, 51.0164, 53.2785, 51.4746, 48.6443, 50.2124, 50.8216, 47.2146, 48.8442, 53.4215, 47.7259, 51.0197, 50.6261, 51.9301, 48.4457, 48.4464, 50.1065, 48.7422, 53.0966, 50.3636, 51.7061, 48.0077, 51.1804, 54.9792, 49.8902, 47.7581, 52.2773, 49.0511, 47.4231, 47.7447, 47.6363, 51.0454, 48.3001, 48.2621, 49.256, 50.8517, 51.7803, 52.4657, 49.3357, 52.0326, 48.4252, 53.4214, 51.2403, 50.5314, 46.8236, 51.9482, 49.0643, 48.7773, 48.3582, 48.7877, 50.9872, 50.8545, 50.1039, 53.7464, 47.5025, 50.5721, 47.3433, 51.1273, 51.1467, 50.2715, 51.0122, 51.1692, 50.9679, 47.2319, 51.7549, 53.4976, 52.2474, 49.4796, 53.932, 50.4944, 52.3447, 51.3655, 52.8856, 46.8156, 48.4145, 51.0485, 49.5219, 49.2729, 53.0077, 48.8778, 50.6185, 50.7531, 49.5473, 45.3727, 46.277, 49.4831, 54.4022, 48.5597, 49.9528, 54.1557, 50.0765, 51.0186, 50.6233, 48.3247, 48.2842, 48.9951, 49.985, 48.5328, 49.0121, 50.5201, 52.3991, 54.7198, 48.5773, 50.5687, 48.0353, 48.7621, 46.5595, 53.4533, 49.148, 51.9194, 47.8909, 45.3101, 53.4165, 46.7687, 47.9325, 52.4055, 50.179, 49.628, 52.6699, 49.4349, 51.6019, 47.5267, 52.8547, 48.8873, 50.3574, 48.5632, 48.3731, 49.4404, 51.8347, 49.5102, 50.389, 49.0566, 52.6478, 47.07, 46.6469, 52.8287, 45.6774, 53.4419, 49.9698, 50.8694, 50.6465, 48.6847, 51.0357, 54.0747, 52.44, 48.5753, 52.4797, 47.6557, 49.5633, 50.2916, 52.4874, 47.2297, 52.1014, 49.1756, 46.723, 51.9951, 49.1672, 45.9931, 51.5935, 46.627, 49.2441, 47.747, 48.3785, 52.1779, 51.2699, 46.984, 49.5491, 50.1363, 52.7129, 50.9212, 47.58, 47.4178, 51.3279, 51.5272, 48.7985, 48.7477, 51.0192, 50.5791, 44.7399, 49.4572, 51.6469, 47.0317, 51.7414, 47.5043, 50.4645, 48.3081, 46.7698, 50.4418, 49.7955, 53.0434, 51.6632, 46.8235, 50.656, 51.9626, 51.6972, 53.7208, 53.9064, 48.5869, 50.7765, 54.8027, 52.5563, 48.6591, 46.9504, 46.8874, 51.3121, 49.7848, 49.6334, 48.8406, 48.2793, 51.2332, 49.9449, 50.8621, 46.7255, 53.5796, 52.3576, 52.1412, 45.8729, 51.7811, 47.1328, 50.8583, 52.3256, 52.1433, 51.4525, 53.7055, 50.3304, 48.3851, 48.9443, 50.0387, 51.6775, 47.8745, 51.6305, 49.9509, 49.0804, 48.1321, 49.0571, 50.6107, 49.6106, 51.5089, 46.7768, 47.0605, 47.9261, 52.2185, 49.7218, 50.0749, 51.1837, 51.3125, 48.9321, 47.1455, 49.283, 46.9428, 49.8658, 48.467, 50.5749, 50.3922, 49.2721, 51.2695, 55.6737, 46.31, 54.8534, 48.0544, 47.2911, 48.0775, 51.4678, 50.4315, 48.76, 48.8405, 48.4808, 51.1831, 52.5098, 49.4894, 48.6413, 49.2476, 45.3848, 49.8913, 50.7658, 49.2616, 48.9906, 52.2424, 50.6268, 50.7753, 51.0062, 50.2866, 52.6272, 47.2609, 49.9375, 45.4337, 49.1591, 54.0067, 47.4308, 47.7936, 49.8504, 51.7266, 51.3603, 48.9126, 53.3951, 49.884, 52.2769, 53.1869, 48.0339, 47.0102, 51.0717, 50.3724, 50.0682, 50.5362, 49.7991, 53.1228, 43.9787, 52.5417, 50.5179, 49.7451, 48.0502, 52.4647, 47.679, 50.2165, 52.2751, 46.69, 50.6322, 50.6216, 50.8536, 49.0698, 51.1711, 52.0772, 49.3992, 54.1668, 47.7272, 48.6428, 52.4052, 49.5666, 48.1262, 49.1069, 53.0292, 51.5877, 48.1473, 46.2535, 52.9845, 50.4912, 49.1547, 48.1848, 45.8552, 47.3573, 50.6006, 49.0126, 53.291, 51.838, 48.8156, 48.1455, 52.5804, 49.2539, 55.2872, 50.1929, 47.27, 45.9186, 48.529, 52.8908, 49.5058, 44.9997, 48.7498, 49.4198, 52.7397, 50.8652, 49.7, 50.6059, 54.4485, 52.1181, 48.5171, 51.3684, 52.2639, 54.6589, 50.5738, 49.3534, 50.7153, 49.0454, 50.3018, 48.39, 50.0468, 47.0863, 51.6879, 51.5971, 46.6984, 51.4943, 48.8792, 52.8256, 52.0087, 52.174, 51.1822, 47.9137, 45.8457, 50.2712, 47.4545, 50.3796, 47.0235, 51.5624, 51.0317, 47.4411, 48.0706, 50.8478, 51.7703, 50.4597, 48.6121, 50.3238, 49.5472, 48.9309, 47.1826, 50.0828, 48.3622, 49.744, 45.9724, 51.6726, 48.8767, 50.1591, 53.0002, 53.1073, 51.3239, 47.9578, 49.8237, 50.6001, 50.5387, 50.281, 51.6408, 51.3167, 52.4379, 51.6453, 48.6104, 49.3623, 49.9748, 50.8257, 49.0165, 49.9787, 49.3364, 48.931, 50.8257, 51.8198, 53.5448, 52.0453, 49.2153, 52.5422, 47.1391, 50.8312, 48.8818, 48.8984, 53.9621, 49.1592, 50.492, 51.1813, 49.9492, 47.278, 50.6562, 49.3153, 49.7884, 49.9536, 50.9123, 50.8216, 46.9631, 50.4396, 53.5282, 46.1847, 48.1475, 48.6202, 46.028, 46.3399, 48.0812, 52.2979, 49.2653, 48.1344, 49.6641, 50.9779, 50.9707, 50.0848, 55.2832, 50.5291, 50.867, 48.9387, 48.277, 47.6711, 47.42, 51.1013, 50.3238, 48.7391, 49.2981, 52.1184, 49.5855, 50.0683, 50.5786, 50.9941, 48.4875, 45.0728, 46.0989, 50.7064, 48.8466, 49.3157, 46.5656, 48.3989, 50.2387, 50.0791, 47.8424, 47.6905, 53.8541, 49.5005, 49.7817, 47.3167, 48.4377, 49.4452, 47.6993, 50.1811, 47.6876, 48.5112, 48.4035, 46.6798, 46.8434, 50.2404, 47.9389, 49.6295, 49.7087, 50.6634, 51.6249, 48.7045, 47.6956, 50.3897, 51.7116, 49.3094, 49.8672, 49.612, 49.6909, 51.2696, 49.1917, 51.2081, 50.8287, 54.3005, 48.5573, 52.3002, 51.6057, 47.5544, 51.1321, 51.2233, 48.7818, 45.0778, 51.4913, 47.4075, 49.6552, 46.4508, 51.1868, 46.3282, 48.8251, 48.8708, 49.9861, 47.0338, 50.9188, 50.9674, 49.2738, 49.4312, 44.494, 53.2105, 49.5465, 51.294, 50.366, 50.1115, 51.3776, 48.7211, 52.0383, 53.1504, 50.4018, 49.6861, 51.9551, 50.8266, 49.25, 49.317, 50.2503, 51.524, 48.9928, 51.58, 53.9251, 51.1853, 49.8744, 51.1382, 49.1157, 47.761, 48.4827, 47.261, 51.9225, 49.0452, 46.299, 51.0216, 50.7256, 50.6074, 47.3176, 47.7647, 53.5239, 48.7906, 50.9114, 50.2461, 47.2285, 47.0514, 50.9909, 49.3797, 49.4353, 52.0836, 46.7541, 49.8797, 52.3428, 51.6863, 49.448, 50.9387, 51.2488, 48.2489, 47.0446, 51.7291, 50.1071, 53.6385, 48.7125, 51.9921, 51.6603, 50.9852, 50.0576, 48.1092, 49.4162, 49.0129, 50.0461, 47.9073, 50.4917, 53.5644, 48.2441, 51.0623, 46.2184, 51.0026, 50.5243, 48.1786, 51.1024, 50.2086, 53.6164, 50.2264, 50.7896, 49.7081, 47.7558, 55.2037, 50.3652, 50.6116, 50.1696, 47.0664, 47.2799, 46.6496, 52.642, 51.0042, 49.3295, 47.1383, 48.3382, 49.7924, 47.5323, 51.7458, 48.3919, 49.4369, 49.799, 45.6233, 51.6184, 50.0804, 45.5683, 49.2182, 49.3319, 47.9309, 50.6977, 52.4089, 50.6933, 47.1778, 48.6446, 46.0435, 50.4012, 48.0035, 50.5509, 51.6023, 51.7564, 50.4155, 49.4884, 46.7571, 54.3625, 54.418, 49.8132, 50.8628, 47.6996, 49.2239, 50.0523, 48.82, 53.5147, 50.6223, 48.7576, 49.1839, 50.487, 46.8165, 50.9643, 49.9208, 51.7969, 50.7108, 48.6935, 48.6604, 52.7723, 50.8573, 51.1914, 51.1877, 49.8188, 47.8101, 53.5978, 49.6131, 52.5765, 49.8216, 52.2145, 48.2675, 47.3676, 49.4412, 49.0352, 51.322, 50.687, 46.9071, 48.5173, 47.2026, 48.5028, 49.814, 48.7582, 52.3331, 50.8368, 48.3012, 52.575, 51.8215, 50.3913, 49.2714, 53.8735, 51.5957, 47.85, 50.3232, 48.3344, 51.7895, 47.9429, 48.7262, 48.7648, 50.9608, 47.9012, 50.0155, 45.6987, 51.7005, 44.6286, 50.3535, 50.4995, 49.0012, 52.1988, 52.2583, 52.1466, 52.2683, 50.6548, 49.2408, 50.5268, 52.2028, 49.8943, 45.1981, 46.6432, 53.6048, 48.3537, 48.7784, 51.889, 51.7934, 51.1171, 50.2889, 45.7557, 54.4371, 49.2422, 52.1792, 50.2835, 48.9294, 49.4659, 49.6628, 53.3018, 47.4139, 50.7936, 53.6124, 48.9808, 50.0719, 47.6513, 53.2911, 47.7328, 51.2918, 46.0668, 48.2755, 50.4445, 47.7727, 49.4893, 47.3196, 50.1779, 47.6357, 47.8788, 50.5313, 46.1912, 51.2621, 48.3671, 53.194, 48.9493, 48.2359, 50.2161, 51.9947, 45.1287, 48.7168, 52.5153, 47.9203, 47.5608, 46.8177, 49.3079, 47.488, 50.8967, 46.9954, 49.0465, 49.6152, 48.7823, 49.5236, 48.0733, 48.22, 52.4572, 50.8477, 50.8449, 52.1104, 50.8073, 49.3888, 51.1591, 49.5086, 53.4911, 50.4182, 50.0926, 53.1264, 51.6498, 48.5938, 50.9567, 47.4401, 49.5051, 50.3511, 50.2807, 49.6691, 48.0892, 50.7983, 51.1111, 48.6509, 49.9555, 50.986, 50.9848, 50.7092, 50.9491, 51.2084, 51.4477, 50.914, 48.9832, 50.67, 48.6653, 48.8311, 50.6844, 50.118, 48.6469, 51.1202, 48.4884, 50.9221, 47.3391, 52.6438, 50.9753, 50.9271, 50.0356, 47.344, 46.6615, 50.1474, 49.258, 50.6538, 52.076, 50.8315, 48.0799, 51.2686, 48.7802, 50.8013, 51.1956, 48.9613, 52.9203, 49.3178, 50.0589, 52.5434, 50.3741, 48.3368, 49.3023, 50.463, 49.113, 48.9836, 51.8943, 49.6128, 53.4761, 49.6782, 47.9582, 49.5093, 51.8623, 48.8425, 52.2357, 50.0141, 50.7905, 48.5486, 50.0649, 49.9885, 52.0972, 49.069, 46.8752, 47.793, 48.6421, 51.2727, 48.2108, 48.041, 49.391, 49.9604, 49.8252, 47.4322, 49.3724, 51.9054, 50.7855, 50.7657, 48.4816, 49.4374, 47.5009, 51.1386, 51.6885, 49.5371, 49.2419, 48.3322, 49.7616, 53.3347, 48.5631, 50.4399, 51.8182, 49.8053, 47.3802, 50.6762, 53.0535, 50.3056, 49.9224, 49.8981, 45.7413, 50.6326, 49.0361, 48.1738, 52.4667, 48.0933, 50.1945, 47.5217, 50.6723, 47.1847, 50.4432, 50.2347, 51.472, 47.4035, 49.1241, 48.352, 51.4409, 46.5386, 49.2162, 50.9725, 50.3482, 46.9289, 52.2261, 50.118, 51.9972, 49.1543, 50.0712, 50.3934, 49.1232, 54.4247, 49.4556, 50.6767, 48.7139, 48.8883, 49.7065, 48.5685, 48.102, 50.0739, 49.1675, 54.254, 49.6341, 48.6799, 51.233, 53.4997, 46.7914, 52.4539, 47.8821, 49.0179, 47.217, 49.0611, 47.2254, 48.7847, 49.3254, 49.7074, 50.508, 50.5292, 49.843, 52.5933, 50.3359, 52.4105, 50.2627, 48.7213, 47.2843, 44.414, 49.1347, 47.6888, 49.953, 52.5628, 45.4812, 51.346, 46.9703, 46.2921, 54.4781, 51.5132, 44.0535, 52.6828, 49.5457, 52.9855, 48.1472, 50.3948, 50.2351, 45.857, 50.257, 51.5934, 48.0865, 51.051, 51.3151, 50.0426, 52.2683, 51.2808, 53.2294, 50.5192, 52.0803, 49.7119, 51.3541, 50.6839, 50.8833, 51.4825, 49.3661, 49.3581, 46.3949, 50.4562, 47.6549, 48.4589, 49.2328, 51.3104, 51.1498, 50.9253, 49.6386, 50.5632, 48.2172, 53.0765, 53.243, 48.7989, 47.5157, 47.6798, 48.144, 48.1359, 50.6079, 50.3623, 51.696, 50.3606, 50.4224, 49.4005, 53.1207, 53.2217, 48.4174, 50.6046, 49.4822, 49.4744, 48.8746, 47.7005, 49.4541, 51.8828, 52.7524, 51.8916, 50.2866, 52.6907, 49.8431, 49.7537, 47.3445, 52.3733, 52.0253, 48.0967, 47.4854, 53.9835, 50.452, 47.8464, 49.383, 51.1531, 52.149, 51.6175, 53.2171, 51.4537, 50.9054, 52.4478, 48.9994, 48.8305, 47.1756, 51.5296, 48.5966, 50.509, 50.2057, 49.3241, 49.1914, 51.9813, 53.2447, 49.1613, 48.4392, 53.8577, 50.1802, 48.8849, 49.3395, 51.7097, 49.827, 50.187, 47.4213, 48.4433, 52.2264, 50.8384, 52.0178, 46.5966, 48.8362, 50.6203, 51.3419, 54.163, 50.4018, 48.2163, 48.4795, 47.3201, 52.6891, 49.6932, 48.7171, 52.2943, 52.6933, 49.3635, 48.9618, 50.5819, 46.6154, 50.9994, 51.3585, 50.3688, 51.4205, 50.033, 50.3458, 52.7756, 50.2246, 53.2498, 51.7159, 49.4732, 49.9156, 48.6467, 52.4338, 55.5517, 49.4904, 50.5942, 50.9719, 50.1855, 49.6683, 51.2726, 47.0755, 48.2534, 50.4724, 50.1337, 50.3984, 51.1713, 52.3949, 47.8988, 48.6397, 52.149, 50.181, 50.3756, 52.4221, 46.3319, 52.9825, 50.3957, 47.4507, 52.4266, 53.5962, 53.0121, 49.4973, 51.3944, 51.074, 47.2617, 55.1253, 48.0139, 54.1316, 49.9402, 50.531, 49.4911, 52.7968, 53.5792, 50.3632, 48.7468, 48.6034, 51.3416, 45.547, 46.784, 50.0211, 50.6148, 49.0342, 49.1374, 46.5561, 49.2196, 49.6897, 50.5844, 51.1482, 46.1037, 53.1298, 45.7766, 51.0327, 49.0991, 51.9672, 51.1042, 49.6212, 49.373, 46.6684, 49.8195, 48.5384, 48.114, 47.7656, 50.0198, 49.156, 51.7416, 48.9164, 47.019, 48.4199, 50.0517, 46.337, 52.6282, 49.5281, 49.725, 50.2564, 51.3227, 51.3105, 48.4786, 48.5556, 53.9343, 45.7802, 47.9698, 48.2215, 51.7986, 50.5568, 50.6262, 48.2504, 50.636, 48.0665, 48.8735, 48.4738, 48.5463, 47.9914, 50.4153, 50.5865, 48.3549, 49.07, 50.8476, 50.2928, 52.8649, 49.4749, 50.6422, 51.3181, 47.9962, 47.8245, 47.2438, 50.8153, 49.5949, 50.5036, 50.2422, 50.4066, 51.2595, 49.1889, 51.4922, 51.2148, 47.2199, 49.5211, 53.091, 57.1777, 49.9096, 50.0491, 46.7184, 50.2232, 49.863, 48.8628, 53.5396, 50.4204, 48.0098, 47.8143, 51.4703, 48.7331, 48.128, 46.6261, 49.7313, 51.1907, 51.6234, 50.4805, 53.2912, 47.6222, 50.3852, 50.7546, 46.7185, 49.6176, 47.8082, 50.4512, 48.3265, 51.3055, 53.1714, 49.5593, 48.701, 47.568, 50.7907, 48.1471, 51.8599, 53.3168, 53.4592, 53.6406, 50.2217, 50.9209, 48.4271, 47.3411, 51.4803, 47.1475, 51.9816, 48.2725, 49.5841, 53.4713, 50.5105, 51.0199, 49.4571, 51.712, 49.4749, 48.137, 48.0201, 46.8704, 47.4818, 51.3413, 49.832, 48.088, 51.5689, 50.3624, 50.8881, 48.0137, 49.0727, 50.6983, 49.4565, 52.0119, 51.5467, 52.995, 50.6116, 48.7175, 50.0919, 54.6537, 52.1717, 49.6928, 51.4241, 49.5901, 49.3579, 52.2623, 50.8541, 51.8696, 53.4952, 49.0096, 50.621, 51.9035, 51.5898, 49.2521, 50.1028, 53.8492, 48.8842, 50.9295, 50.989, 47.1357, 51.2998, 47.3129, 50.4294, 52.4694, 49.0071, 46.9611, 49.9527, 49.9611, 50.2414, 50.454, 50.5681, 47.0727, 52.1483, 45.6657, 49.2896, 51.6117, 48.472, 47.0969, 48.4947, 49.0263, 54.0101, 47.0114, 50.8417, 49.5989, 48.368, 47.2363, 47.6237, 50.6863, 51.3495, 52.63, 48.0883, 45.756, 51.1516, 51.0532, 52.37, 46.2127, 52.9934, 50.2617, 54.0357, 49.5813, 52.7816, 48.841, 54.3012, 48.9732, 46.9671, 50.4766, 52.2835, 52.5345, 49.8787, 50.7978, 49.9765, 52.6926, 52.0947, 48.9933, 51.945, 49.9587, 52.3751, 49.2421, 50.1836, 48.8622, 46.8715, 47.5701, 52.2489, 51.24, 48.4173, 50.9849, 50.0665, 51.5444, 49.1144, 49.9808, 47.6968, 52.2937, 51.0484, 50.237, 54.8901, 52.358, 52.8596, 50.6656, 48.9737, 50.9147, 50.1043, 49.3226, 52.3793, 49.3779, 50.509, 50.2566, 47.0026, 51.0732, 51.5475, 50.1101, 50.518, 49.3666, 48.7561, 48.75, 46.4049, 51.0791, 47.3483, 49.1067, 45.8302, 48.6726, 51.0895, 50.183, 50.0626, 50.4792, 46.6195, 51.7473, 49.1619, 50.063, 50.8733, 44.6904, 52.8463, 49.32, 51.1157, 49.3117, 48.7672, 52.6381, 47.593, 50.5872, 48.9391, 51.0323, 51.798, 51.3882, 51.1148, 48.8007, 49.5072, 50.665, 52.4082, 50.9411, 49.62, 50.4238, 51.8298, 50.9366, 51.7994, 49.1971, 51.8163, 49.2855, 50.1893, 51.1523, 47.3411, 49.4923, 50.5476, 50.3325, 51.6614, 45.3539, 46.6746, 51.9889, 51.48, 53.8921, 49.7682, 52.3949, 54.4525, 45.4291, 46.6849, 49.0037, 47.1591, 53.0304, 49.4685, 48.5995, 52.4717, 51.3049, 48.8344, 51.3667, 48.1571, 48.6041, 52.3568, 48.3103, 49.0002, 49.0769, 48.8757, 44.9091, 46.8817, 50.302, 52.7519, 51.37, 48.9616, 49.7447, 46.6443, 49.4868, 49.5194, 48.8172, 51.1899, 50.7834, 51.1024, 54.4057, 51.0696, 46.0581, 47.2083, 49.8796, 48.2185, 49.4949, 52.8282, 50.1599, 47.5791, 52.8617, 49.594, 49.8952, 50.4253, 51.0264, 48.5285, 50.1212, 52.4871, 52.4633, 48.3626, 50.0318, 53.0496, 49.4787, 47.3127, 48.7044, 51.1941, 49.7941, 50.078, 50.5211, 49.4442, 51.0034, 52.6537, 50.8516, 51.0499, 52.2471, 51.4611, 52.2641, 45.9555, 50.0693, 50.8039, 49.7296, 48.2934, 49.7346, 48.5245, 50.6749, 51.3546, 49.5195, 52.1276, 48.951, 48.801, 50.1595, 49.1989, 51.9859, 49.6541, 51.0662, 51.8765, 46.7427, 49.8916, 50.2399, 46.3063, 50.1081, 49.6148, 47.1159, 52.6604, 50.251, 45.4947, 49.0482, 49.3515, 49.7745, 48.474, 47.8582, 50.4978, 48.068, 51.9285, 50.9753, 50.4736, 48.8619, 50.2226, 50.119, 50.2986, 46.7848, 49.9859, 45.4463, 50.2448, 54.9396, 48.0545, 48.3791, 51.6591, 48.3151, 47.9358, 49.5271, 50.3052, 51.7371, 49.0677, 48.7957, 49.4734, 48.5859, 50.5983, 52.7357, 48.3374, 47.6879, 49.8602, 48.8359, 47.1395, 47.3349, 46.2875, 49.866, 48.516, 53.5774, 50.788, 47.9565, 47.7976, 49.0667, 47.2757, 49.8431, 52.1996, 49.357, 48.6094, 46.0551, 47.0516, 50.1714, 49.7355, 50.3779, 47.1489, 51.7454, 48.9954, 46.3138, 47.7836, 48.7149, 49.8496, 46.669, 51.7517, 49.9694, 51.6964, 52.8715, 49.4088, 48.7286, 51.1527, 46.525, 52.1564, 49.8792, 49.1456, 47.7291, 47.3853, 49.3797, 50.9203, 49.9632, 45.3876, 48.6243, 51.0266, 53.6701, 49.7001, 50.761, 50.0075, 49.1924, 47.6972, 48.1649, 47.3171, 50.9268, 51.7019, 49.6217, 48.7263, 52.0189, 54.4329, 46.5045, 51.1197, 50.2744, 50.6411, 51.3843, 53.0778, 49.7467, 54.3059, 47.6405, 51.5227, 47.1417, 47.1624, 50.8378, 51.4399, 48.8422, 51.1188, 48.5473, 52.2221, 47.0392, 50.4138, 50.5722, 50.4202, 50.0027, 49.7591, 44.8504, 47.065, 51.9949, 51.3897, 52.7157, 50.124, 53.3742, 49.7216, 52.0617, 49.0633, 48.8983, 51.8098, 47.1074, 53.05, 53.685, 49.9872, 52.7572, 48.9282, 49.5041, 50.475, 48.8933, 51.4079, 51.7865, 51.301, 48.9742, 46.9534, 50.8337, 49.2922, 46.7663, 47.5908, 49.4292, 53.0474, 50.7621, 48.2406, 47.8411, 48.0143, 47.9602, 50.3205, 52.2249, 55.4133, 53.5307, 49.0352, 51.8799, 50.1972, 53.4819, 51.0295] \ No newline at end of file diff --git a/skills/cloud/agent-platform-alert-configuration/references/has_historical_traffic_data.md b/skills/cloud/agent-platform-alert-configuration/references/has_historical_traffic_data.md new file mode 100644 index 0000000000..0bc3631e2e --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/references/has_historical_traffic_data.md @@ -0,0 +1,47 @@ +# Has Historical Traffic Data Available + +Use these instructions if the agent has historical metrics data available: + +## 1. Run Traffic Analyzer Script + +- Run the `analyze_traffic.py` script to classify the metrics traffic pattern + profile using one of the following commands: + - **Live Query**: `python3 scripts/analyze_traffic.py --live --project-id + [PROJECT_ID] --reasoning-engine-id [REASONING_ENGINE_ID]` + - **Metrics File**: `python3 scripts/analyze_traffic.py --metrics-file + [PATH_TO_JSON]` +- **Handling Tool Failures**: If the `--live` command fails with + `CredentialsMissingError` (exit code 1), report the error and instruct the + user to run `gcloud auth application-default login` on their terminal. +- Map the traffic pattern profile classified by the script to **Latency**: + - **Steady**: Maps to **Long-Window Z-Score Baseline (1-week lookback)** + (safe since the script verified we have at least 14 days of history). + - **Seasonal**: Maps to **Seasonal Decomposition** (average 1w and 1d). + - **Bursty**: Maps to **Moving Averages** (1h baseline). +- **Fallback for Insufficient Data / No Traffic**: + - If the script fails with a `ValueError` indicating insufficient data + points (less than 14 days of history), or if it outputs "New Agent / No + Traffic" (inactive agent), you MUST fallback to the user inquiry + instructions in + [no_historical_traffic_data.md](no_historical_traffic_data.md) to ask + the user for the expected traffic pattern. +- Regardless of the script's output profile, the other policies MUST use their + correct data-class defaults: + - **Error Rate**: ALWAYS use **Multi-Window Multi-Burn Rate SLO Alerting** + (or ratio-based static limits). + +## 2. User Notification + +Clearly communicate the findings and selection at the start of your response: + +1. Explain the classified traffic profile (Seasonal, Steady, or Bursty) output + by the metrics analysis script (citing indicators like standard deviation, + autocorrelation, or zero-ratio from the script output). If falling back to + user inquiry due to zero metrics or insufficient data, explain that. +2. Propose the corresponding alerting policy mapping (Latency matching the + traffic profile, Error Rate using SLO Burn Rate). +3. Ask the user if this expected profile mapping is correct or if they would + like to customize standard deviation thresholds. +4. Provide a brief plain-English explanation of what each of the proposed + alerts measures and how the underlying algorithms work and what they + actually measure. Keep this explanation in the conversational response text. diff --git a/skills/cloud/agent-platform-alert-configuration/references/no_historical_traffic_data.md b/skills/cloud/agent-platform-alert-configuration/references/no_historical_traffic_data.md new file mode 100644 index 0000000000..cff9198aea --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/references/no_historical_traffic_data.md @@ -0,0 +1,69 @@ +# No Historical Traffic Data Available + +Use these instructions if there is no historical metrics data available for the +agent (e.g., brand new agent): + +## 1. Ask the User for the Traffic Pattern and Handle Defaults + +- Because no historical metrics data is available and we do NOT perform + traffic pattern inference based on the name, description, or context of the + agent, you MUST ask the user directly what traffic pattern they expect for + their agent. +- Present the user with the three options for customization: + - **Steady/Consistent**: (e.g., continuous background tasks, sync jobs, + daemons). Maps **Latency** to **Short-Window Z-Score Baseline (1-hour + lookback)**. + - **Bursty/Inconsistent**: (e.g., periodic background tasks, batch worker, + cron jobs). Maps **Latency** to **Moving Averages (1-hour baseline)**. + - **Seasonal/Cyclical**: (e.g., user-facing systems, support portals, + chatbots). Maps **Latency** to **Seasonal Decomposition** (requires + offsets `1d` and `1w`). +- Inform the user that the default traffic pattern is **Steady/Consistent** + (which maps to Short-Window Z-Score Baseline), and that you will use this + default if they do not have a good idea or do not specify one. +- **Handling Automated or Immediate Setup Requests**: If the user's prompt + asks you to configure or write the alerting policies immediately (e.g., "Set + up its alerting policies in 'monitoring/alerts.tf'"), or if you are running + in an automated/non-interactive script, you MUST NOT pause to wait for their + response. Instead, ask the question in your response, state that you are + deploying the default Steady/Consistent pattern because no choice was + specified yet, and **immediately proceed to generate and write the default + configuration (Steady / Consistent -> Short-Window Z-Score)**. +- Regardless of the selected traffic pattern, the other policies MUST use + their correct data-class defaults: + + - **Error Rate**: ALWAYS use **Multi-Window Multi-Burn Rate SLO Alerting** + (or ratio-based static limits). + +* **Short-Window Z-Score / Moving Averages**: Require **1 hour** of traffic + history. + +* **SLO Burn Rate (Error Rate)**: Requires up to **3 days** for the slow burn + component, though the fast burn component (1h/5m) will work after 1 hour. + +* **Seasonal Decomposition**: Requires **1 week** of history (due to the `1w` + offset). **WARNING:** If the user switches to Seasonal Decomposition, warn + them that they will have a 1-week blind spot, and suggest starting with + **Short-Window Z-Score** or **Static Thresholds** as a temporary guard. + +## 2. User Notification + +Clearly communicate the lack of historical data, explain the options, and detail +the immediate actions taken at the start of your response: + +1. Explain that since the agent has no historic data, you cannot automatically + analyze the traffic pattern. +2. Ask the user directly what traffic pattern they expect (Steady, Seasonal, or + Bursty), detailing the mapping differences and the 1-week blind spot risk if + they choose Seasonal. +3. Inform the user that the default is **Steady / Consistent** (Short-Window + Z-Score algorithm for Latency) and you will proceed with this default if + they don't have a good idea or do not choose. +4. If the user accepts the default, explain that you have deployed the + Steady/Consistent default to ensure the files are configured immediately, + but they can request an update if they prefer another pattern. +5. Explain the warm-up periods (1 hour for Latency, up to 3 days for SLOs). +6. Propose the rest of the configuration mapping: Error Rate (SLO Burn Rate). +7. Provide a brief plain-English explanation of what each of the proposed + alerts measures and how the underlying algorithms work and what they + actually measure. diff --git a/skills/cloud/agent-platform-alert-configuration/references/promql_queries.md b/skills/cloud/agent-platform-alert-configuration/references/promql_queries.md new file mode 100644 index 0000000000..a4a0c3730c --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/references/promql_queries.md @@ -0,0 +1,132 @@ +# PromQL Queries Reference + +This file contains the recommended PromQL queries and template configurations +for monitoring Latency and Error Rates of Agent Platform agents. + +## Table of Contents + +- [1. Latency (95th Percentile)](#1-latency-95th-percentile) + - [Z-Score (Steady Traffic)](#z-score-recommended-for-steady-traffic) + - [Moving Averages (Bursty Traffic)](#moving-averages-recommended-for-bursty-traffic) + - [Seasonal Decomposition (Seasonal Traffic)](#seasonal-decomposition-recommended-for-traffic-with-seasonal-or-time-of-day-component) +- [2. Error Rate (SLO)](#2-error-rate-slo) + - [Fast Burn SLO](#fast-burn-slo-1-hour-and-5-minute-windows) + - [Slow Burn SLO](#slow-burn-slo-3-day-and-6-hour-windows) + +-------------------------------------------------------------------------------- + +## 1. Latency (95th Percentile) + +### Z-Score (Recommended for Steady Traffic) + +#### Long-Window Z-Score (For Established Agents - >1 week history) + +Compares the 5-minute 95th percentile latency to the 1-week baseline. + +```promql +abs( + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m])) by (le, reasoning_engine_id)) + - + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[1w])) by (le, reasoning_engine_id)) +) +/ +stddev_over_time( + (histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m])) by (le, reasoning_engine_id)))[1w:5m] +) > 3 +``` + +*Note: The denominator uses a subquery `[1w:5m]` to calculate standard deviation +of the 5-minute latency over 1 week. The numerator uses `[1w]` rate directly to +avoid a second subquery for the mean.* + +#### Short-Window Z-Score (For Newer Agents - >1 hour history) + +Compares the 1-minute 95th percentile latency to the 1-hour baseline. Useful for +quick activation on new agents. + +```promql +abs( + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[1m])) by (le, reasoning_engine_id)) + - + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[1h])) by (le, reasoning_engine_id)) +) +/ +stddev_over_time( + (histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[1m])) by (le, reasoning_engine_id)))[1h:1m] +) > 3 +``` + +### Moving Averages (Recommended for Bursty Traffic) + +Compares the 5-minute latency to the 1-hour average. + +```promql +histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m])) by (le, reasoning_engine_id)) +> +1.5 * histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[1h])) by (le, reasoning_engine_id)) +``` + +### Seasonal Decomposition (Recommended for traffic with seasonal or time-of-day component) + +> [!NOTE] For the Latency alert policy, ONLY use seasonal decomposition to track +> Latency spikes. Alert policies using seasonal decomposition tracking both +> spikes and drops can falsely trigger alerts. + +Compares the 5-minute latency to the average of 1-week and 1-day lookback +baselines. + +```promql +histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m])) by (le, reasoning_engine_id)) +/ +( + ( + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m] offset 1d)) by (le, reasoning_engine_id)) + + + histogram_quantile(0.95, sum(rate(aiplatform_googleapis_com:reasoning_engine_request_latencies_bucket[5m] offset 1w)) by (le, reasoning_engine_id)) + ) / 2 +) +> 2 +``` + +-------------------------------------------------------------------------------- + +## 2. Error Rate (SLO) + +Always use Multi-Window Multi-Burn Rate SLOs. Z-score is not recommended due to +sparsity. + +### Fast Burn SLO (1-Hour and 5-Minute Windows) + +```promql +( + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2.."}[5m])) by (reasoning_engine_id) + / + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m])) by (reasoning_engine_id) + > (1 - ${var.slo_target}) * 14.4 +) +and +( + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2.."}[1h])) by (reasoning_engine_id) + / + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[1h])) by (reasoning_engine_id) + > (1 - ${var.slo_target}) * 14.4 +) +``` + +### Slow Burn SLO (3-Day and 6-Hour Windows) + +```promql +( + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2.."}[6h])) by (reasoning_engine_id) + / + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[6h])) by (reasoning_engine_id) + > (1 - ${var.slo_target}) * 1.0 +) +and +( + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2.."}[3d])) by (reasoning_engine_id) + / + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[3d])) by (reasoning_engine_id) + > (1 - ${var.slo_target}) * 1.0 +) +``` diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic.py b/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic.py new file mode 100644 index 0000000000..fb05530328 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Traffic metrics analyzer for Agent Platform reasoning engine agents. + +Computes zero-ratio, variance ratio, and 1-week autocorrelation on a 14-day +metrics time series (at 5-minute intervals) and classifies the traffic pattern +to recommend the optimal dynamic PromQL alerting threshold strategy. +""" + +import argparse +import json +import math +import statistics +import sys +import time + +try: + from google.auth import exceptions as auth_exceptions + from google.cloud import monitoring_v3 + + _HAS_GCP_LIBS = True +except ImportError: + _HAS_GCP_LIBS = False + +# Number of 5-minute data points in a 7-day weekly window +# (12 points/hour * 24 hours/day * 7 days/week) +_POINTS_PER_WEEK = 2016 + + +class CredentialsMissingError(Exception): + """Raised when Google Cloud credentials are not found.""" + + pass + + +def compute_metrics_and_classify(values): + """Computes statistical metrics and classifies the traffic profile. + + Args: + values: A list of floats representing the 5-minute rate telemetry. Should + be at least 4032 points (14 days). + + Returns: + A dictionary with statistical metrics and the classification profile. + """ + required_points = 2 * _POINTS_PER_WEEK + if len(values) < required_points: + raise ValueError( + f"Insufficient data points: expected at least {required_points} (14" + f" days at 5m interval), got {len(values)}" + ) + + # We focus on the last 7 days (latest weekly points) and previous 7 days (weekly points before that) + last_7_days = values[-_POINTS_PER_WEEK:] + prev_7_days = values[-required_points:-_POINTS_PER_WEEK] + + # 1. Zero Ratio: proportion of 5m intervals with rate <= 0.01 in the last 7 days + zero_ratio = sum(x <= 0.01 for x in last_7_days) / _POINTS_PER_WEEK + + # 2. Mean and Standard Deviation of the last 7 days + mean_last = statistics.mean(last_7_days) + stddev_last = statistics.pstdev(last_7_days) + + variance_ratio = stddev_last / mean_last if mean_last > 0.0001 else 0.0 + + # 3. Autocorrelation at 1-week lag + mean_prev = statistics.mean(prev_7_days) + stddev_prev = statistics.pstdev(prev_7_days) + + if stddev_last > 0.0001 and stddev_prev > 0.0001: + covariance = ( + sum( + (x - mean_last) * (y - mean_prev) + for x, y in zip(last_7_days, prev_7_days) + ) + / _POINTS_PER_WEEK + ) + autocorr_1w = covariance / (stddev_last * stddev_prev) + else: + autocorr_1w = 0.0 + + # Decision tree classification + if mean_last == 0.0 and zero_ratio == 1.0: + profile = "New Agent / No Traffic" + algorithm = "Short-Window Z-Score (1h baseline)" + rationale = ( + "No historical traffic observed. Defaulting to Short-Window Z-Score" + " (1h baseline) dynamic thresholding to ensure quick activation" + " (requires 1 hour of traffic history)." + ) + elif variance_ratio > 2.0: + profile = "Bursty / Inconsistent" + algorithm = "Moving Averages" + rationale = ( + f"Variance ratio is high ({variance_ratio:.2f}), indicating volatile" + " bursty peaks. Moving averages dynamic baseline thresholds are" + " recommended to smooth out short-term fluctuations and prevent false" + " pages." + ) + elif autocorr_1w > 0.75 and variance_ratio <= 2.0: + profile = "Seasonal / Cyclical" + algorithm = "Seasonal Decomposition (average 1w and 1d)" + rationale = ( + f"Auto-correlation at 1w lag is high ({autocorr_1w:.2f}) with stable" + f" variance ratio ({variance_ratio:.2f}). Comparing against the average" + " of 1w and 1d offsets mitigates holiday false positives and prevents" + " diurnal false alerts during off-peak periods." + ) + else: + profile = "Steady / Consistent" + algorithm = "1w Z-Score Baseline" + rationale = ( + "Stable regular traffic pattern with low variance ratio" + f" ({variance_ratio:.2f}) and moderate/low autocorrelation" + f" ({autocorr_1w:.2f}). 1w Z-Score baseline is ideal." + ) + + return { + "zero_ratio": round(zero_ratio, 4), + "variance_ratio": round(variance_ratio, 4), + "autocorr_1w": round(autocorr_1w, 4), + "mean": round(mean_last, 4), + "stddev": round(stddev_last, 4), + "profile": profile, + "recommended_algorithm": algorithm, + "rationale": rationale, + } + + +def align_to_grid( + points: list[tuple[float, float]], + end_time: float, + num_points: int = 4032, + interval_sec: int = 300, +) -> list[float]: + """Aligns sparse telemetry points to a uniform time grid of fixed size. + + We anchor the grid at end_time aligned to interval_sec and look backward. + + Args: + points: A list of tuples (timestamp, value). + end_time: The end timestamp of the grid window. + num_points: The exact number of points in the target grid. + interval_sec: The grid interval in seconds. + + Returns: + A list of floats representing the aligned values in order of time. + """ + aligned_end = math.floor(end_time / interval_sec) * interval_sec + aligned_start = aligned_end - num_points * interval_sec + + grid = [0.0] * num_points + + for ts, val in points: + rounded_ts = round(ts / interval_sec) * interval_sec + idx = int((rounded_ts - aligned_start) / interval_sec) - 1 + if 0 <= idx < num_points: + grid[idx] += val + + return grid + + +def query_live_metrics( + project_id: str, reasoning_engine_id: str +) -> list[float]: + """Queries live Vertex AI reasoning engine request count metrics for 14 days. + + Args: + project_id: The GCP project ID. + reasoning_engine_id: The reasoning engine ID. + + Returns: + A list of 4032 floats representing the 5m rate telemetry aligned to grid. + + Raises: + CredentialsMissingError: If GCP credentials are not found. + """ + if not _HAS_GCP_LIBS: + raise RuntimeError("Google Cloud Client libraries are not installed.") + + try: + client = monitoring_v3.MetricServiceClient() + name = f"projects/{project_id}" + + now = time.time() + seconds_per_day = 24 * 3600 + start_time = now - 14 * seconds_per_day + + interval = monitoring_v3.TimeInterval({ + "end_time": {"seconds": int(now)}, + "start_time": {"seconds": int(start_time)}, + }) + + filter_str = ( + "metric.type =" + ' "aiplatform.googleapis.com/reasoning_engine/request_count" AND' + ' resource.type = "aiplatform.googleapis.com/ReasoningEngine" AND' + f' resource.labels.reasoning_engine_id = "{reasoning_engine_id}"' + ) + + results = client.list_time_series( + request={ + "name": name, + "filter": filter_str, + "interval": interval, + "view": monitoring_v3.ListTimeSeriesRequest.TimeSeriesView.FULL, + "aggregation": { + "alignment_period": {"seconds": 300}, + "per_series_aligner": ( + monitoring_v3.Aggregation.Aligner.ALIGN_RATE + ), + "cross_series_reducer": ( + monitoring_v3.Aggregation.Reducer.REDUCE_SUM + ), + "group_by_fields": ["resource.labels.reasoning_engine_id"], + }, + } + ) + except auth_exceptions.DefaultCredentialsError as e: + raise CredentialsMissingError( + "No valid Google Cloud credentials found. Please run 'gcloud auth" + " application-default login' to authenticate your local environment." + ) from e + + points = [] + for series in results: + for point in series.points: + points.append(( + point.interval.start_time.ToDatetime().timestamp(), + point.value.double_value, + )) + + if not points: + # No traffic detected at all. Return a 14-day grid filled with 0.0. + return [0.0] * (2 * _POINTS_PER_WEEK) + + # Align sparse points to the 14-day 5-minute grid + return align_to_grid(points, now, num_points=2 * _POINTS_PER_WEEK) + + +def main(): + parser = argparse.ArgumentParser( + description=( + "Analyze 14-day traffic metrics to select dynamic thresholding" + " algorithm." + ) + ) + parser.add_argument( + "--metrics-file", + type=str, + help="Path to JSON file containing list of numbers. Use '-' for stdin.", + ) + parser.add_argument( + "--live", + action="store_true", + help="Force live GCP metrics query validation check.", + ) + parser.add_argument( + "--project-id", + type=str, + help="GCP project ID (required for live queries).", + ) + parser.add_argument( + "--reasoning-engine-id", + type=str, + help=( + "Vertex AI Reasoning Engine numerical ID (required for live queries)." + ), + ) + args = parser.parse_args() + + data = None + + if args.live: + if not args.project_id or not args.reasoning_engine_id: + print( + "Error: Live GCP queries require --project-id and" + " --reasoning-engine-id to be specified.", + file=sys.stderr, + ) + sys.exit(1) + + try: + data = query_live_metrics(args.project_id, args.reasoning_engine_id) + except CredentialsMissingError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error executing live query: {e}", file=sys.stderr) + sys.exit(1) + + else: + # File/Stdin input mode + if not args.metrics_file and sys.stdin.isatty(): + print( + "Error: Must specify --metrics-file or pipe metrics data to stdin" + " when not using --live.", + file=sys.stderr, + ) + sys.exit(1) + + try: + if args.metrics_file == "-" or not args.metrics_file: + data = json.load(sys.stdin) + else: + with open(args.metrics_file, "r") as f: + data = json.load(f) + except Exception as e: + print(f"Error reading metrics data: {e}", file=sys.stderr) + sys.exit(1) + + if not data or not isinstance(data, list): + print( + "Error: Metrics data must be a JSON list of numbers or parsed" + " successfully from a source", + file=sys.stderr, + ) + sys.exit(1) + + try: + results = compute_metrics_and_classify(data) + print(json.dumps(results, indent=2)) + except ValueError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic_test.py b/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic_test.py new file mode 100644 index 0000000000..2b69c93e93 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/analyze_traffic_test.py @@ -0,0 +1,180 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for metrics data time-series parsing. + +Also tests decision tree traffic profiling decisions. +""" + +import datetime +import json +import os +import sys +import time +import unittest +from unittest import mock + +import analyze_traffic + + +class AnalyzeTrafficTest(unittest.TestCase): + + def setUp(self): + super().setUp() + self.mock_data_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets" + ) + if not os.path.exists(self.mock_data_dir): + raise FileNotFoundError( + f"Could not find mock data directory: {self.mock_data_dir}" + ) + + def _load_mock_data(self, filename): + filepath = os.path.join(self.mock_data_dir, filename) + with open(filepath, "r") as f: + return json.load(f) + + def test_classify_seasonal(self): + data = self._load_mock_data("mock_seasonal.json") + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Seasonal / Cyclical") + self.assertEqual( + results["recommended_algorithm"], + "Seasonal Decomposition (average 1w and 1d)", + ) + + def test_classify_steady(self): + data = self._load_mock_data("mock_steady.json") + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Steady / Consistent") + self.assertEqual(results["recommended_algorithm"], "1w Z-Score Baseline") + + def test_classify_bursty(self): + data = self._load_mock_data("mock_bursty.json") + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Bursty / Inconsistent") + self.assertEqual(results["recommended_algorithm"], "Moving Averages") + + def test_classify_new_agent(self): + data = [0.0] * 4032 + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "New Agent / No Traffic") + self.assertEqual( + results["recommended_algorithm"], "Short-Window Z-Score (1h baseline)" + ) + self.assertIn("No historical traffic observed", results["rationale"]) + + def test_insufficient_data(self): + with self.assertRaises(ValueError): + analyze_traffic.compute_metrics_and_classify([1.0] * 100) + + def test_classify_constant_traffic(self): + # Constant 10.0 traffic should be classified as Steady/Consistent + data = [10.0] * 4032 + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Steady / Consistent") + self.assertEqual(results["recommended_algorithm"], "1w Z-Score Baseline") + + def test_classify_very_low_constant_traffic(self): + # Constant 0.005 traffic (below 0.01 zero threshold) should still be Steady/Consistent + # due to mean_last being non-zero (0.005 != 0.0) + data = [0.005] * 4032 + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Steady / Consistent") + self.assertEqual(results["recommended_algorithm"], "1w Z-Score Baseline") + + def test_classify_near_zero_spike_falls_to_steady(self): + # 4031 zeros and a single 0.05 value. mean_last is 0.05/2016 = 0.0000248 <= 0.0001, + # so variance_ratio is forced to 0.0, and it falls back to Steady/Consistent. + data = [0.0] * 4031 + [0.05] + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Steady / Consistent") + self.assertEqual(results["recommended_algorithm"], "1w Z-Score Baseline") + + def test_classify_single_spike_bursty(self): + # 4031 zeros and a single 0.5 value. mean_last is 0.5/2016 = 0.000248 > 0.0001, + # so variance_ratio is computed (~44.88 > 2.0) and it classifies as Bursty/Inconsistent. + data = [0.0] * 4031 + [0.5] + results = analyze_traffic.compute_metrics_and_classify(data) + self.assertEqual(results["profile"], "Bursty / Inconsistent") + self.assertEqual(results["recommended_algorithm"], "Moving Averages") + + @mock.patch("sys.exit") + @mock.patch("builtins.print") + @mock.patch("google.cloud.monitoring_v3.MetricServiceClient") + def test_main_live_query(self, mock_client_cls, mock_print, mock_exit): + # Ensure google.cloud.monitoring_v3 module import doesn't fail + mock_client = mock_client_cls.return_value + + class MockTimestamp: + + def __init__(self, seconds): + self.seconds = seconds + + def ToDatetime(self): + return datetime.datetime.fromtimestamp( + self.seconds, tz=datetime.timezone.utc + ) + + class MockPoint: + + def __init__(self, seconds, val): + self.interval = mock.MagicMock() + self.interval.start_time = MockTimestamp(seconds) + self.value = mock.MagicMock() + self.value.double_value = val + + class MockSeries: + + def __init__(self, points): + self.points = points + + now = time.time() + start_ts = now - 14 * 24 * 3600 + mock_points = [ + MockPoint(start_ts + (i + 1) * 300, 10.0) for i in range(4032) + ] + mock_client.list_time_series.return_value = [MockSeries(mock_points)] + + test_argv = [ + "analyze_traffic.py", + "--live", + "--project-id", + "my-project", + "--reasoning-engine-id", + "123", + ] + with mock.patch.object(sys, "argv", test_argv): + analyze_traffic.main() + + mock_print.assert_called() + printed_str = mock_print.call_args[0][0] + results = json.loads(printed_str) + self.assertEqual(results["profile"], "Steady / Consistent") + self.assertEqual(results["recommended_algorithm"], "1w Z-Score Baseline") + + def test_align_to_grid(self): + points = [ + (0.0, 1.0), + (10.0, 1.5), + (600.0, 2.0), + (2700.0, 3.0), + (3000.0, 4.0), + ] + grid = analyze_traffic.align_to_grid(points, 3000.0, num_points=10) + self.assertEqual(grid, [0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 4.0]) + + +if __name__ == "__main__": + unittest.main() diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor.py b/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor.py new file mode 100644 index 0000000000..53c11d57d1 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor.py @@ -0,0 +1,170 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper script to create a Vertex AI Reasoning Engine Online Monitor.""" + +import argparse +import importlib +import sys + +try: + aiplatform_v1beta1 = importlib.import_module( + "google.cloud.aiplatform_v1beta1" + ) +except ModuleNotFoundError: + aiplatform_v1beta1 = importlib.import_module( + "google.cloud.aiplatform.aiplatform_v1beta1" + ) + +EXPECTED_METRICS = { + "hallucination_v1", + "final_response_quality_v1", + "tool_use_quality_v1", +} + + +def verify_configuration(evaluator, target_sampling_percentage) -> bool: + """Checks if the existing monitor config matches the desired settings.""" + actual_metrics = set() + for metric_source in evaluator.metric_sources: + spec = metric_source.metric.predefined_metric_spec + if spec and spec.metric_spec_name: + actual_metrics.add(spec.metric_spec_name) + + if actual_metrics != EXPECTED_METRICS: + return False + + config = evaluator.config + actual_sampling = 10 # Platform Default + if config and config.random_sampling: + actual_sampling = config.random_sampling.percentage + + if int(actual_sampling) != int(target_sampling_percentage): + return False + + return True + + +def create_agent_online_monitor( + project_id: str, + location: str, + agent_resource_name: str, + sampling_percentage: int = 10, +) -> str: + """Checks for matching monitor and creates one if none exists.""" + client = aiplatform_v1beta1.OnlineEvaluatorServiceClient( + client_options={"api_endpoint": f"{location}-aiplatform.googleapis.com"} + ) + + parent = f"projects/{project_id}/locations/{location}" + + # 1. Look for a monitor that matches our config exactly + request = aiplatform_v1beta1.ListOnlineEvaluatorsRequest(parent=parent) + page_result = client.list_online_evaluators(request=request) + + matching_evaluator = None + for evaluator in page_result: + if evaluator.agent_resource == agent_resource_name: + if verify_configuration(evaluator, sampling_percentage): + matching_evaluator = evaluator + break + + if matching_evaluator: + print( + f"Found matching Online Monitor: {matching_evaluator.name}. No action" + " needed." + ) + return matching_evaluator.name + + # 2. If no matching monitor is found, create a new one + print("No matching Online Monitor found for agent. Creating a new one...") + cloud_obs_spec = aiplatform_v1beta1.OnlineEvaluator.CloudObservability + online_evaluator = aiplatform_v1beta1.OnlineEvaluator( + display_name="agent-quality-monitor", + agent_resource=agent_resource_name, + cloud_observability=cloud_obs_spec( + open_telemetry=cloud_obs_spec.OpenTelemetry( + semconv_version="gen_ai_latest_experimental" + ), + trace_scope=cloud_obs_spec.TraceScope(), + ), + metric_sources=[ + aiplatform_v1beta1.MetricSource( + metric=aiplatform_v1beta1.Metric( + predefined_metric_spec=aiplatform_v1beta1.PredefinedMetricSpec( + metric_spec_name=m + ) + ) + ) + for m in EXPECTED_METRICS + ], + config=aiplatform_v1beta1.OnlineEvaluator.Config( + random_sampling=( + aiplatform_v1beta1.OnlineEvaluator.Config.RandomSampling( + percentage=sampling_percentage + ) + ) + ), + ) + + request = aiplatform_v1beta1.CreateOnlineEvaluatorRequest( + parent=parent, + online_evaluator=online_evaluator, + ) + + print(f"Creating Online Monitor for agent: {agent_resource_name}...") + operation = client.create_online_evaluator(request=request) + response = operation.result() + print(f"Online Monitor created successfully: {response.name}") + return response.name + + +def main(): + parser = argparse.ArgumentParser( + description="Provision a Vertex AI Reasoning Engine Online Monitor." + ) + parser.add_argument("--project-id", required=True, help="The GCP Project ID.") + parser.add_argument( + "--location", default="us-central1", help="The GCP Location/Region." + ) + parser.add_argument( + "--agent-resource-name", + required=True, + help="The full resource path of the Reasoning Engine agent.", + ) + parser.add_argument( + "--sampling-percentage", + type=int, + default=10, + help=( + "The percentage of incoming traces to evaluate (1-100). Default" + " is 10." + ), + ) + + args = parser.parse_args() + try: + create_agent_online_monitor( + project_id=args.project_id, + location=args.location, + agent_resource_name=args.agent_resource_name, + sampling_percentage=args.sampling_percentage, + ) + except Exception as e: + print(f"Error creating online monitor: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor_test.py b/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor_test.py new file mode 100644 index 0000000000..b33d7a612b --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/create_online_monitor_test.py @@ -0,0 +1,187 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for Vertex AI Reasoning Engine Online Monitor creation.""" + +import os +import sys +import types +import unittest +from unittest import mock + +script_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "create_online_monitor.py" +) +with open(script_path, "r") as f: + code_content = f.read() + +create_online_monitor = types.ModuleType("create_online_monitor") +create_online_monitor.__file__ = script_path +sys.modules["create_online_monitor"] = create_online_monitor +exec(code_content, create_online_monitor.__dict__) + + +class CreateOnlineMonitorTest(unittest.TestCase): + + @mock.patch( + "google.cloud.aiplatform.aiplatform_v1beta1.OnlineEvaluatorServiceClient" + ) + def test_create_agent_online_monitor_already_exists_noop( + self, mock_client_class + ): + mock_client = mock_client_class.return_value + + mock_evaluator = mock.Mock() + mock_evaluator.name = ( + "projects/my-project/locations/us-central-test/onlineEvaluators/999" + ) + mock_evaluator.agent_resource = ( + "projects/my-project/locations/us-central-test/reasoningEngines/123" + ) + + mock_metric_sources = [] + for metric_name in [ + "hallucination_v1", + "final_response_quality_v1", + "tool_use_quality_v1", + ]: + m = mock.Mock() + m.metric.predefined_metric_spec.metric_spec_name = metric_name + mock_metric_sources.append(m) + mock_evaluator.metric_sources = mock_metric_sources + mock_evaluator.config.random_sampling.percentage = 15 + + mock_client.list_online_evaluators.return_value = [mock_evaluator] + + evaluator_name = create_online_monitor.create_agent_online_monitor( + project_id="my-project", + location="us-central-test", + agent_resource_name=( + "projects/my-project/locations/us-central-test/reasoningEngines/123" + ), + sampling_percentage=15, + ) + + self.assertEqual(evaluator_name, mock_evaluator.name) + mock_client.create_online_evaluator.assert_not_called() + + @mock.patch( + "google.cloud.aiplatform.aiplatform_v1beta1.OnlineEvaluatorServiceClient" + ) + def test_create_agent_online_monitor_not_exists_creates_new( + self, mock_client_class + ): + mock_client = mock_client_class.return_value + mock_client.list_online_evaluators.return_value = [] + + mock_operation = mock.Mock() + mock_client.create_online_evaluator.return_value = mock_operation + + mock_response = mock.Mock() + mock_response.name = ( + "projects/my-project/locations/us-central-test/onlineEvaluators/999" + ) + mock_operation.result.return_value = mock_response + + evaluator_name = create_online_monitor.create_agent_online_monitor( + project_id="my-project", + location="us-central-test", + agent_resource_name=( + "projects/my-project/locations/us-central-test/reasoningEngines/123" + ), + sampling_percentage=15, + ) + + # Verify Endpoint and Instantiation + mock_client_class.assert_called_once_with( + client_options={ + "api_endpoint": "us-central-test-aiplatform.googleapis.com" + } + ) + + # Verify API Call + mock_client.create_online_evaluator.assert_called_once() + call_args = mock_client.create_online_evaluator.call_args[1] + request = call_args["request"] + + self.assertEqual( + request.parent, "projects/my-project/locations/us-central-test" + ) + self.assertEqual( + request.online_evaluator.display_name, "agent-quality-monitor" + ) + self.assertEqual( + request.online_evaluator.agent_resource, + "projects/my-project/locations/us-central-test/reasoningEngines/123", + ) + self.assertEqual( + request.online_evaluator.config.random_sampling.percentage, 15 + ) + + # Verify Metric Names are set + metrics = [ + m.metric.predefined_metric_spec.metric_spec_name + for m in request.online_evaluator.metric_sources + ] + self.assertIn("hallucination_v1", metrics) + self.assertIn("final_response_quality_v1", metrics) + self.assertIn("tool_use_quality_v1", metrics) + + self.assertEqual(evaluator_name, mock_response.name) + + @mock.patch( + "google.cloud.aiplatform.aiplatform_v1beta1.OnlineEvaluatorServiceClient" + ) + def test_create_agent_online_monitor_failure(self, mock_client_class): + mock_client = mock_client_class.return_value + mock_client.list_online_evaluators.return_value = [] + mock_client.create_online_evaluator.side_effect = Exception("API error") + + with self.assertRaises(Exception) as context: + create_online_monitor.create_agent_online_monitor( + project_id="my-project", + location="us-central-test", + agent_resource_name=( + "projects/my-project/locations/us-central-test/" + "reasoningEngines/123" + ), + ) + self.assertIn("API error", str(context.exception)) + + @mock.patch("create_online_monitor.create_agent_online_monitor") + def test_main_cli_parsing(self, mock_create): + test_args = [ + "create_online_monitor.py", + "--project-id", + "cli-proj", + "--agent-resource-name", + "cli-agent", + "--sampling-percentage", + "30", + "--location", + "us-east4", + ] + with mock.patch.object(sys, "argv", test_args): + create_online_monitor.main() + + mock_create.assert_called_once_with( + project_id="cli-proj", + location="us-east4", + agent_resource_name="cli-agent", + sampling_percentage=30, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/validate_config.py b/skills/cloud/agent-platform-alert-configuration/scripts/validate_config.py new file mode 100644 index 0000000000..da7c30f5d6 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/validate_config.py @@ -0,0 +1,370 @@ +#!/usr/bin/env python3 +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""HCL configuration and PromQL validation script for agent platform metrics. + +Parses Terraform HCL alert policy resource blocks, detects duplicate targets +for reasoning engines, and lints Prometheus queries for correct syntax, time +windows, and essential label filters. +""" + +import argparse +import glob +import json +import os +import re +import sys + + +def check_balanced_chars(query, open_char, close_char): + """Checks if parenthesis or braces are balanced.""" + count = 0 + for i, char in enumerate(query): + if char == open_char: + count += 1 + elif char == close_char: + count -= 1 + if count < 0: + return f"Unbalanced '{close_char}' at position {i}" + if count != 0: + return f"Unbalanced '{open_char}' (net count: {count})" + return None + + +def lint_query(query): + """Runs a suite of sanity lint checks on a PromQL query. + + Args: + query: The PromQL query string to lint. + + Returns: + A list of string lint error messages. Empty if valid. + """ + errors = [] + + # 1. Balanced parentheses + paren_err = check_balanced_chars(query, "(", ")") + if paren_err: + errors.append(f"Parentheses error: {paren_err}") + + # 2. Balanced curly braces + brace_err = check_balanced_chars(query, "{", "}") + if brace_err: + errors.append(f"Curly braces error: {brace_err}") + + # 3. Time window validations (e.g., [5m], [1w:5m], [3d], [1h]) + window_matches = re.finditer(r"\[([^\]]+)\]", query) + for match in window_matches: + window_str = match.group(1) + if not re.match(r"^\d+[smhdw](:(\d+[smhdw])?)?$", window_str): + errors.append( + "Invalid Prometheus time window/subquery interval:" + f" '[{window_str}]' at position {match.start()}" + ) + + # 4. Lookback offset range validation (e.g. offset 1w, offset 1d) + offset_matches = re.finditer(r"\boffset\s+(\S+)", query) + for match in offset_matches: + offset_str = match.group(1) + if not re.match(r"^\d+[smhdw]$", offset_str): + errors.append( + f"Invalid lookback offset format: 'offset {offset_str}' at position" + f" {match.start()}" + ) + + # 5. Ensure the query references reasoning_engine_id in a label filter + # or grouping aggregation. + has_group = bool( + re.search(r"\b(by|without)\s*\([^)]*reasoning_engine_id[^)]*\)", query) + ) + + has_filter = False + brace_matches = re.finditer(r"\{([^}]+)\}", query) + for match in brace_matches: + if "reasoning_engine_id" in match.group(1): + has_filter = True + break + + if not (has_group or has_filter): + errors.append( + "Query is missing 'reasoning_engine_id' reference. It must either" + " group by 'reasoning_engine_id' using aggregations (e.g., 'by" + " (reasoning_engine_id)') or filter on it (e.g.," + " '{reasoning_engine_id=\"...\"}')." + ) + + return errors + + +def extract_alert_policies(hcl_content): + """Extracts resource 'google_monitoring_alert_policy' blocks and metadata.""" + policies = [] + pattern = re.compile( + r'resource\s+"google_monitoring_alert_policy"\s+"([^"]+)"\s*\{' + ) + + for match in pattern.finditer(hcl_content): + resource_name = match.group(1) + start_pos = match.start() + + brace_count = 0 + end_pos = -1 + in_string = False + escape = False + + for i in range(match.end() - 1, len(hcl_content)): + char = hcl_content[i] + if escape: + escape = False + continue + if char == "\\": + escape = True + continue + if char == '"': + in_string = not in_string + continue + if not in_string: + if char == "{": + brace_count += 1 + elif char == "}": + brace_count -= 1 + if brace_count == 0: + end_pos = i + 1 + break + + if end_pos == -1: + continue + + block_content = hcl_content[start_pos:end_pos] + + # Extract display_name + display_name_match = re.search( + r'display_name\s*=\s*"([^"]+)"', block_content + ) + display_name = display_name_match.group(1) if display_name_match else "" + + # Extract PromQL queries + queries = [ + q.group(1) + for q in re.finditer( + r"query\s*=\s*<<-?EOT\n(.*?)\n\s*EOT", + block_content, + re.DOTALL, + ) + ] + if not queries: + for match in re.finditer( + r"query\s*=\s*\"((?:[^\"\\]|\\[\s\S])*)\"", block_content + ): + raw_query = match.group(1) + clean_query = re.sub(r"\\+\"", '"', raw_query).replace("\\\\", "\\") + queries.append(clean_query) + + # Extract threshold filters + filters = [] + filter_matches = re.finditer( + r'filter\s*=\s*"((?:[^"\\]|\\.)*)"', block_content + ) + for f_match in filter_matches: + filters.append(f_match.group(1)) + + # Infer signal type + signal_type = "unknown" + res_lower, disp_lower = resource_name.lower(), display_name.lower() + rules = [ + ("latency", "latency", "latency"), + ("slo_burn_rate_fast", "fast", "slo_fast"), + ("slo_burn_rate_slow", "slow", "slo_slow"), + ] + for res_pat, disp_pat, sig in rules: + if res_pat in res_lower or disp_pat in disp_lower: + signal_type = sig + break + else: + # Check threshold filters for quality metric name + for flt in filters: + metric_match = re.search( + r"metric\.labels\.evaluation_metric_name\s*=\s*\\*\"([^\"\\]+)\\*\"", + flt, + ) + if metric_match: + signal_type = metric_match.group(1) + break + + engine_ids = [] + for query in queries: + for engine_id in re.findall( + r"reasoning_engine_id\s*=\s*\"([^\"]+)\"", query + ): + if engine_id not in engine_ids: + engine_ids.append(engine_id) + + for flt in filters: + id_matches = re.findall( + r'reasoning_engine_id\s*=\s*\\*"([^"\\]+)\\*"', flt + ) + for engine_id in id_matches: + if engine_id not in engine_ids: + engine_ids.append(engine_id) + resource_matches = re.findall(r"reasoningEngines/([0-9]+)", flt) + for engine_id in resource_matches: + if engine_id not in engine_ids: + engine_ids.append(engine_id) + + policies.append({ + "resource_name": resource_name, + "display_name": display_name, + "signal_type": signal_type, + "engine_ids": engine_ids, + "queries": queries, + "filters": filters, + "start_pos": start_pos, + "end_pos": end_pos, + "block_content": block_content, + }) + + return policies + + +def validate_directory_tf_files(directory, expected_engine_var=None): + """Scans and validates all *.tf files in a given directory.""" + tf_files = glob.glob(os.path.join(directory, "*.tf")) + all_errors = [] + all_policies = [] + duplicates = [] + + target_map = {} + + for filepath in tf_files: + filename = os.path.basename(filepath) + try: + with open(filepath, "r") as f: + content = f.read() + except Exception as e: + all_errors.append(f"File error in '{filename}': {e}") + continue + + policies = extract_alert_policies(content) + for policy in policies: + policy["filename"] = filename + all_policies.append(policy) + + for query in policy["queries"]: + lint_errs = lint_query(query) + for err in lint_errs: + all_errors.append( + f"Lint error in '{filename}' -> resource" + f" '{policy['resource_name']}': {err}" + ) + + engine_key = ( + policy["engine_ids"][0] + if policy["engine_ids"] + else expected_engine_var or "default" + ) + key = (engine_key, policy["signal_type"]) + + if key not in target_map: + target_map[key] = [] + target_map[key].append(policy) + + for (engine, signal_type), matches in target_map.items(): + if len(matches) > 1 and signal_type != "unknown": + duplicates.append({ + "engine_id": engine, + "signal_type": signal_type, + "policies": [ + { + "filename": p["filename"], + "resource_name": p["resource_name"], + "display_name": p["display_name"], + } + for p in matches + ], + }) + + for dup in duplicates: + policy_list = ", ".join( + f"'{p['resource_name']}' in '{p['filename']}'" for p in dup["policies"] + ) + all_errors.append( + "Duplicate Target Error: Multiple alert policies are targeting the" + f" same engine '{dup['engine_id']}' and signal '{dup['signal_type']}':" + f" [{policy_list}]. Please apply the in-place upgrade protocol instead" + " of appending new blocks!" + ) + + return { + "valid": len(all_errors) == 0, + "errors": all_errors, + "policies_scanned_count": len(all_policies), + "duplicates_found": duplicates, + } + + +def main(): + parser = argparse.ArgumentParser( + description=( + "Lints HCL alerts and PromQL query targets in standard tf templates." + ) + ) + parser.add_argument( + "--directory", + type=str, + default=".", + help="Directory containing *.tf files to scan.", + ) + parser.add_argument( + "--engine-var", + type=str, + default="${var.reasoning_engine_id}", + help="The expected variable or literal for the reasoning engine ID.", + ) + parser.add_argument( + "--file", + type=str, + help="Validate a single specific HCL file instead of scanning directory.", + ) + args = parser.parse_args() + + if args.file: + try: + with open(args.file, "r") as f: + content = f.read() + policies = extract_alert_policies(content) + errors = [] + for p in policies: + for q in p["queries"]: + errors.extend(lint_query(q)) + if errors: + print(f"Validation failed for '{args.file}':", file=sys.stderr) + for err in errors: + print(f" - {err}", file=sys.stderr) + sys.exit(1) + else: + print(f"Validation passed for '{args.file}'!") + sys.exit(0) + except Exception as e: + print(f"Error reading file '{args.file}': {e}", file=sys.stderr) + sys.exit(1) + + results = validate_directory_tf_files(args.directory, args.engine_var) + print(json.dumps(results, indent=2)) + if not results["valid"]: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cloud/agent-platform-alert-configuration/scripts/validate_config_test.py b/skills/cloud/agent-platform-alert-configuration/scripts/validate_config_test.py new file mode 100644 index 0000000000..838489df69 --- /dev/null +++ b/skills/cloud/agent-platform-alert-configuration/scripts/validate_config_test.py @@ -0,0 +1,289 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for HCL configuration alert policies parsing. + +Also tests duplicate detection, and PromQL query syntax validation linting. +""" + +import os +import tempfile +import unittest + +import validate_config + + +class ValidateConfigTest(unittest.TestCase): + + def test_lint_query_valid(self): + query = ( + "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m]))" + " by (reasoning_engine_id)" + ) + self.assertEqual(validate_config.lint_query(query), []) + + def test_lint_query_unbalanced_parentheses(self): + query = ( + "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m]))" + " by (reasoning_engine_id" + ) + errors = validate_config.lint_query(query) + self.assertTrue(any("Parentheses error" in e for e in errors)) + + def test_lint_query_unbalanced_braces(self): + query = ( + 'sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{response_code!~"2.."[5m]))' + " by (reasoning_engine_id)" + ) + errors = validate_config.lint_query(query) + self.assertTrue(any("Curly braces error" in e for e in errors)) + + def test_lint_query_invalid_window(self): + for invalid_suffix in ("5x", "5y"): + query = ( + "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count" + f"[{invalid_suffix}])) by (reasoning_engine_id)" + ) + errors = validate_config.lint_query(query) + self.assertTrue( + any("Invalid Prometheus time window" in e for e in errors) + ) + + def test_lint_query_valid_subquery_intervals(self): + queries = [ + # With resolution + ( + "avg_over_time((sum(rate(" + "aiplatform_googleapis_com:reasoning_engine_request_count[5m]" + ")) by (reasoning_engine_id))[1w:5m])" + ), + # Without resolution + ( + "avg_over_time((sum(rate(" + "aiplatform_googleapis_com:reasoning_engine_request_count[5m]" + ")) by (reasoning_engine_id))[1w:])" + ), + ] + for query in queries: + self.assertEqual(validate_config.lint_query(query), []) + + def test_lint_query_invalid_subquery_intervals(self): + queries = [ + # Invalid resolution format: number only (no unit) + ( + "avg_over_time((sum(rate(" + "aiplatform_googleapis_com:reasoning_engine_request_count[5m]" + ")) by (reasoning_engine_id))[1w:5])" + ), + # Invalid resolution format: unit only (no number) + ( + "avg_over_time((sum(rate(" + "aiplatform_googleapis_com:reasoning_engine_request_count[5m]" + ")) by (reasoning_engine_id))[1w:m])" + ), + ] + for query in queries: + errors = validate_config.lint_query(query) + self.assertTrue( + any("Invalid Prometheus time window" in e for e in errors) + ) + + def test_lint_query_missing_reference(self): + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m]))" + errors = validate_config.lint_query(query) + self.assertTrue( + any("missing 'reasoning_engine_id' reference" in e for e in errors) + ) + + def test_lint_query_valid_with_filter(self): + query = 'sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id="12345"}[5m]))' + self.assertEqual(validate_config.lint_query(query), []) + + def test_lint_query_valid_with_regex_or_prefix_filter(self): + query = ( + 'sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id!~"dev-.*"}[5m]))' + " by (reasoning_engine_id)" + ) + self.assertEqual(validate_config.lint_query(query), []) + + def test_scanner_extract_valid_hcl(self): + hcl_content = """ + resource "google_monitoring_alert_policy" "agent_latency_anomaly" { + project = var.project_id + display_name = "[Agent Alert] Latency Anomaly - ${var.agent_name}" + combiner = "OR" + + conditions { + display_name = "p95 Latency exceeds 3x Standard Deviation (1w baseline)" + condition_prometheus_query_language { + query = <<-EOT + sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m])) by (reasoning_engine_id) + EOT + duration = "300s" + } + } + } + """ + policies = validate_config.extract_alert_policies(hcl_content) + self.assertEqual(len(policies), 1) + self.assertEqual(policies[0]["resource_name"], "agent_latency_anomaly") + self.assertEqual(policies[0]["signal_type"], "latency") + + def test_scanner_extract_escaped_inline_query(self): + hcl_content = r""" + resource "google_monitoring_alert_policy" "agent_latency_anomaly" { + display_name = "[Agent Alert] Latency Anomaly - ${var.agent_name}" + conditions { + condition_prometheus_query_language { + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id=\"12345\"}[5m]))" + } + } + } + """ + policies = validate_config.extract_alert_policies(hcl_content) + self.assertEqual(len(policies), 1) + self.assertEqual(len(policies[0]["queries"]), 1) + self.assertEqual( + policies[0]["queries"][0], + 'sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id="12345"}[5m]))', + ) + self.assertEqual(policies[0]["engine_ids"], ["12345"]) + + hcl_content_three_backslash = r""" + resource "google_monitoring_alert_policy" "agent_latency_anomaly" { + display_name = "[Agent Alert] Latency Anomaly - ${var.agent_name}" + conditions { + condition_prometheus_query_language { + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id=\\\"12345\\\"}[5m]))" + } + } + } + """ + policies_three = validate_config.extract_alert_policies( + hcl_content_three_backslash + ) + self.assertEqual(len(policies_three), 1) + self.assertEqual(len(policies_three[0]["queries"]), 1) + self.assertEqual( + policies_three[0]["queries"][0], + 'sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count{reasoning_engine_id="12345"}[5m]))', + ) + self.assertEqual(policies_three[0]["engine_ids"], ["12345"]) + + def test_validator_detects_duplicates(self): + with tempfile.TemporaryDirectory() as tmpdir: + tf_content_1 = """ + resource "google_monitoring_alert_policy" "agent_latency_anomaly_1" { + display_name = "[Agent Alert] Latency Anomaly - ${var.agent_name}" + conditions { + condition_prometheus_query_language { + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m])) by (reasoning_engine_id)" + } + } + } + """ + tf_content_2 = """ + resource "google_monitoring_alert_policy" "agent_latency_anomaly_2" { + display_name = "[Agent Alert] Latency Anomaly - Alternative" + conditions { + condition_prometheus_query_language { + query = "sum(rate(aiplatform_googleapis_com:reasoning_engine_request_count[5m])) by (reasoning_engine_id)" + } + } + } + """ + with open(os.path.join(tmpdir, "policy1.tf"), "w") as f: + f.write(tf_content_1) + with open(os.path.join(tmpdir, "policy2.tf"), "w") as f: + f.write(tf_content_2) + + results = validate_config.validate_directory_tf_files( + tmpdir, "${var.reasoning_engine_id}" + ) + self.assertFalse(results["valid"]) + self.assertEqual(len(results["duplicates_found"]), 1) + self.assertEqual(results["duplicates_found"][0]["signal_type"], "latency") + + def test_scanner_extracts_quality_metric_signal_type(self): + hcl_content = """ + resource "google_monitoring_alert_policy" "agent_final_response_quality" { + project = var.project_id + display_name = "Agent Final Response Quality (Median < 0.8)" + combiner = "OR" + enabled = true + + conditions { + display_name = "Final Response Quality Score" + condition_threshold { + filter = "resource.type=\\"aiplatform.googleapis.com/OnlineEvaluator\\" AND metric.type=\\"aiplatform.googleapis.com/online_evaluator/scores\\" AND metric.labels.evaluation_metric_name=\\"final_response_quality_v1\\"" + comparison = "COMPARISON_LT" + threshold_value = 0.8 + duration = "300s" + aggregations { + alignment_period = "300s" + per_series_aligner = "ALIGN_PERCENTILE_50" + } + trigger { + count = 1 + } + } + } + } + """ + policies = validate_config.extract_alert_policies(hcl_content) + self.assertEqual(len(policies), 1) + self.assertEqual( + policies[0]["resource_name"], "agent_final_response_quality" + ) + self.assertEqual(policies[0]["signal_type"], "final_response_quality_v1") + + def test_validator_detects_quality_duplicates(self): + with tempfile.TemporaryDirectory() as tmpdir: + tf_content_1 = """ + resource "google_monitoring_alert_policy" "q1" { + display_name = "Quality Alert 1" + conditions { + condition_threshold { + filter = "resource.type=\\"aiplatform.googleapis.com/OnlineEvaluator\\" AND metric.type=\\"aiplatform.googleapis.com/online_evaluator/scores\\" AND metric.labels.evaluation_metric_name=\\"tool_use_quality_v1\\"" + } + } + } + """ + tf_content_2 = """ + resource "google_monitoring_alert_policy" "q2" { + display_name = "Quality Alert 2" + conditions { + condition_threshold { + filter = "resource.type=\\"aiplatform.googleapis.com/OnlineEvaluator\\" AND metric.type=\\"aiplatform.googleapis.com/online_evaluator/scores\\" AND metric.labels.evaluation_metric_name=\\"tool_use_quality_v1\\"" + } + } + } + """ + with open(os.path.join(tmpdir, "policy1.tf"), "w") as f: + f.write(tf_content_1) + with open(os.path.join(tmpdir, "policy2.tf"), "w") as f: + f.write(tf_content_2) + + results = validate_config.validate_directory_tf_files( + tmpdir, "${var.reasoning_engine_id}" + ) + self.assertFalse(results["valid"]) + self.assertEqual(len(results["duplicates_found"]), 1) + self.assertEqual( + results["duplicates_found"][0]["signal_type"], "tool_use_quality_v1" + ) + + +if __name__ == "__main__": + unittest.main()