Muse Spark 1.1·续1

Here's a small, strict version. It fixes the original bug where `item[key] === undefined` silently created a `"undefined"` bucket.

### Generic implementation

**Chosen behavior for missing keys:** If `item[key]` is `null` or `undefined`, the item is **skipped** and not included in any group. Rationale: avoids polluting the result with a coerced `"undefined"` key. If you need to keep them, normalize before calling.

```ts
// groupBy.ts
type PropertyKey = string | number | symbol;

// Only allow keys whose value can be a property key (or optional version)
type GroupableKeys<T> = {
  [K in keyof T]: T[K] extends PropertyKey | null | undefined ? K : never
}[keyof T];

export function groupBy<T, K extends GroupableKeys<T>>(
  items: readonly T[],
  key: K
): Record<string, T[]> {
  const out: Record<string, T[]> = {};

for (const item of items) {
    const raw = item[key];
    if (raw == null) continue; // <-- explicit missing-key policy

const groupKey = String(raw as PropertyKey);
    (out[groupKey] ??= []).push(item);
  }

return out;
}
```

> Typed to `Record<string, T[]>` intentionally. If you want literal key inference you can change the return to `Record<Extract<T[K], PropertyKey> & {}, T[]>` but for a library this is more ergonomic and doesn't lie about runtime `String()` coercion.

### Tests that document behavior

```ts
// groupBy.test.ts
import { describe, it, expect } from 'vitest';
import { groupBy } from './groupBy';

type Item = { cat?: string; id: number };

describe('groupBy', () => {
  it('empty input -> empty object', () => {
    expect(groupBy([] as Item[], 'cat')).toEqual({});
  });

it('missing key value -> item skipped', () => {
    const items: Item[] = [
      { cat: 'a', id: 1 },
      { id: 2 }, // cat is missing
      { cat: undefined, id: 3 }, // cat is explicitly undefined
    ];

const result = groupBy(items, 'cat');

// only 'a' group exists, no "undefined" bucket
    expect(result).toEqual({
      a: [{ cat: 'a', id: 1 }],
    });
    expect(Object.keys(result)).not.toContain('undefined');
  });

it('two items sharing a key -> grouped together', () => {
    const items: Item[] = [
      { cat: 'x', id: 1 },
      { cat: 'x', id: 2 },
      { cat: 'y', id: 3 },
    ];

const result = groupBy(items, 'cat');

expect(result).toEqual({
      x: [
        { cat: 'x', id: 1 },
        { cat: 'x', id: 2 },
      ],
      y: [{ cat: 'y', id: 3 }],
    });
  });
});
```
{
  "id": "resp_6ac7b0009eadac3fd3fd4b77",
  "object": "response",
  "created_at": 1791471616,
  "model": "muse-spark-1.1",
  "output": [
    {
      "type": "reasoning",
      "id": "rs_6ac7b0009eadac3fd3fd4b77:rs_70691cf1-3099-4eaf-8a65-6c9811ee8913",
      "summary": [],
      "status": "completed"
    },
    {
      "type": "message",
      "id": "msg_20968e1e-1419-4226-89cb-2c6497d11b9e",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "Here's a small, strict version. It fixes the original bug where `item[key] === undefined` silently created a `\"undefined\"` bucket.\n\n### Generic implementation\n\n**Chosen behavior for missing keys:** If `item[key]` is `null` or `undefined`, the item is **skipped** and not included in any group. Rationale: avoids polluting the result with a coerced `\"undefined\"` key. If you need to keep them, normalize before calling.\n\n```ts\n// groupBy.ts\ntype PropertyKey = string | number | symbol;\n\n// Only allow keys whose value can be a property key (or optional version)\ntype GroupableKeys&lt;T> = {\n  [K in keyof T]: T[K] extends PropertyKey | null | undefined ? K : never\n}[keyof T];\n\nexport function groupBy&lt;T, K extends GroupableKeys&lt;T>>(\n  items: readonly T[],\n  key: K\n): Record&lt;string, T[]> {\n  const out: Record&lt;string, T[]> = {};\n\n  for (const item of items) {\n    const raw = item[key];\n    if (raw == null) continue; // <-- explicit missing-key policy\n\n    const groupKey = String(raw as PropertyKey);\n    (out[groupKey] ??= []).push(item);\n  }\n\n  return out;\n}\n```\n\n> Typed to `Record<string, T[]>` intentionally. If you want literal key inference you can change the return to `Record<Extract<T[K], PropertyKey> & {}, T[]>` but for a library this is more ergonomic and doesn't lie about runtime `String()` coercion.\n\n### Tests that document behavior\n\n```ts\n// groupBy.test.ts\nimport { describe, it, expect } from 'vitest';\nimport { groupBy } from './groupBy';\n\ntype Item = { cat?: string; id: number };\n\ndescribe('groupBy', () => {\n  it('empty input -> empty object', () => {\n    expect(groupBy([] as Item[], 'cat')).toEqual({});\n  });\n\n  it('missing key value -> item skipped', () => {\n    const items: Item[] = [\n      { cat: 'a', id: 1 },\n      { id: 2 }, // cat is missing\n      { cat: undefined, id: 3 }, // cat is explicitly undefined\n    ];\n\n    const result = groupBy(items, 'cat');\n\n    // only 'a' group exists, no \"undefined\" bucket\n    expect(result).toEqual({\n      a: [{ cat: 'a', id: 1 }],\n    });\n    expect(Object.keys(result)).not.toContain('undefined');\n  });\n\n  it('two items sharing a key -> grouped together', () => {\n    const items: Item[] = [\n      { cat: 'x', id: 1 },\n      { cat: 'x', id: 2 },\n      { cat: 'y', id: 3 },\n    ];\n\n    const result = groupBy(items, 'cat');\n\n    expect(result).toEqual({\n      x: [\n        { cat: 'x', id: 1 },\n        { cat: 'x', id: 2 },\n      ],\n      y: [{ cat: 'y', id: 3 }],\n    });\n  });\n});\n```",
          "annotations": [],
          "logprobs": []
        }
      ],
      "status": "completed"
    }
  ],
  "status": "completed",
  "usage": {
    "input_tokens": 127,
    "output_tokens": 3158,
    "total_tokens": 3285,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens_details": {
      "reasoning_tokens": 2479
    }
  },
  "top_logprobs": 0,
  "temperature": 1,
  "top_p": 1,
  "service_tier": "auto",
  "reasoning": {
    "effort": "high"
  },
  "background": false,
  "tools": [],
  "tool_choice": "auto",
  "truncation": "disabled",
  "completed_at": 1791471616,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "parallel_tool_calls": true,
  "store": true,
  "max_output_tokens": 4096
}

</details>

<details>

<summary>Production Incident Triage — Triage a production incident from sparse symptoms and produce an ordered investigation plan.</summary>

const response = await env.AI.run(
  'meta/muse-spark-1.1',
  {
    messages: [
      {
        role: 'system',
        content:
          'You are an incident commander. Separate facts from hypotheses, prioritize customer impact, and give reversible next steps before risky remediation.',
      },
      {
        role: 'user',
        content:
          'At 14:05 UTC, API p95 latency rose from 180 ms to 2.4 s for one region. Error rate is 0.8%, database CPU is 45%, cache hit rate fell from 92% to 61%, and deploys have been quiet for six hours. A single endpoint is responsible for 70% of the extra latency. Write an incident update, list the three highest-value checks in order, and propose a safe mitigation.',
      },
    ],
    max_completion_tokens: 4096,
  },
)
console.log(response)
curl https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/run \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "model": "meta/muse-spark-1.1",
  "input": {
    "messages": [
      {
        "role": "system",
        "content": "You are an incident commander. Separate facts from hypotheses, prioritize customer impact, and give reversible next steps before risky remediation."
      },
      {
        "role": "user",
        "content": "At 14:05 UTC, API p95 latency rose from 180 ms to 2.4 s for one region. Error rate is 0.8%, database CPU is 45%, cache hit rate fell from 92% to 61%, and deploys have been quiet for six hours. A single endpoint is responsible for 70% of the extra latency. Write an incident update, list the three highest-value checks in order, and propose a safe mitigation."
      }
    ],
    "max_completion_tokens": 4096
  }
}'
### INCIDENT UPDATE #1 - 14:20 UTC - API Latency Elevation [Single Region]

**Status:** Investigating - SEV2
**Impact:** Customers served from `us-east-1 [example]` experiencing API p95 of 2.4s vs normal 180ms since 14:05 UTC. p50 stable. Error rate still low at 0.8% but we are seeing timeouts. Other regions healthy.
**Scope:** ~70% of added latency is isolated to a single endpoint: `GET /v1/<hot-endpoint>`

**FACTS:**
* 14:05 UTC start, sustained. Single region only.
* p95: 180ms -> 2.4s. Error rate: 0.8%
* Cache hit rate: 92% -> 61% in that region at same time.
* DB CPU 45% [normal], no saturation.
* No deploys for 6 hours to API or infra.
* One endpoint accounts for 70% of the latency increase.

**WORKING HYPOTHESES [Unconfirmed - Do not act on alone]:**
* Cache tier degradation / node loss in that region causing miss storm -> origin overload for that endpoint.
* Data / traffic shape change for that endpoint causing key cardinality blow-up / cache stampede.
* Regional capacity issue - client pool saturation, instance brownout, network partition to cache.

**NOT doing right now:** Cache flush, DB failover/restart, new deploy/rollback. All are high-risk and destructive to evidence.

---
### Top 3 Checks - In Priority Order

**1. Cache Tier Health in Affected Region - Highest signal**
Owner: Cache / SRE. Timebox: 5 min.
* Are all cache nodes members? Any node down/evicted at 14:05?
* Memory pressure, evictions/sec, OOMs, CPU, client connections, network errors.
* Client-side errors from API to cache, not just server-side.

**2. Hot Endpoint Traffic Analysis**
Owner: Backend. Timebox: 5 min.
* Traces for `/v1/<hot-endpoint>`: QPS change at 14:05? Tenant / customer spike?
* Cache key pattern: Hit rate per key, new keys per second, payload size. Is there a single hot key or key explosion?
* Origin latency without cache - is the DB / downstream for this endpoint spiking?

**3. Regional Serving Health**
Owner: SRE
* Pod/instance count, crash loops, autoscaler events, CPU throttling/GC for API fleet in that region at 14:05.
* Service mesh / load balancer latency between API and cache tier.
* Breakdown of that 0.8% errors: 504 vs 500 vs 429?

### Safe, Reversible Mitigation Proposed

Goal: Protect customers while preserving evidence.

**Proposal: Traffic shed + regional shift:**
1.  Put a **temporal rate limit / load shed** on `GET /v1/<hot-endpoint>` in affected region only - e.g., 50% of normal limit, return 429 with Retry-After. Protects other endpoints.
2.  **Shift 50% of API traffic out of affected region** to healthy regions via Global LB weight change. 1-click reversible.
3.  Enable request coalescing + stale-while-revalidate for that endpoint if feature-flag exists.

**Why safe:** No data loss, no restarts, instantly reversible via config/LB. Directly addresses symptom - cache miss storm - without touching DB/cache.

**Rollback:** Revert LB weight + remove rate limit.

Next update in 20 minutes or upon completion of check #1.

Please attach cache and endpoint dashboards in thread.
{
  "id": "resp_6ac7b01785cd56aad50246a5",
  "object": "response",
  "created_at": 1791471639,
  "model": "muse-spark-1.1",
  "output": [
    {
      "type": "reasoning",
      "id": "rs_6ac7b01785cd56aad50246a5:rs_322771d5-5cdd-4e2d-a99d-9f26fb95b8a9",
      "summary": [],
      "status": "completed"
    },
    {
      "type": "message",
      "id": "msg_f39c4926-6745-4033-90d7-961b45acd92b",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "### INCIDENT UPDATE #1 - 14:20 UTC - API Latency Elevation [Single Region]\n\n**Status:** Investigating - SEV2\n**Impact:** Customers served from `us-east-1 [example]` experiencing API p95 of 2.4s vs normal 180ms since 14:05 UTC. p50 stable. Error rate still low at 0.8% but we are seeing timeouts. Other regions healthy.\n**Scope:** ~70% of added latency is isolated to a single endpoint: `GET /v1/<hot-endpoint>`\n\n**FACTS:**\n* 14:05 UTC start, sustained. Single region only.\n* p95: 180ms -> 2.4s. Error rate: 0.8%\n* Cache hit rate: 92% -> 61% in that region at same time.\n* DB CPU 45% [normal], no saturation.\n* No deploys for 6 hours to API or infra.\n* One endpoint accounts for 70% of the latency increase.\n\n**WORKING HYPOTHESES [Unconfirmed - Do not act on alone]:**\n* Cache tier degradation / node loss in that region causing miss storm -> origin overload for that endpoint.\n* Data / traffic shape change for that endpoint causing key cardinality blow-up / cache stampede.\n* Regional capacity issue - client pool saturation, instance brownout, network partition to cache.\n\n**NOT doing right now:** Cache flush, DB failover/restart, new deploy/rollback. All are high-risk and destructive to evidence.\n\n---\n### Top 3 Checks - In Priority Order\n\n**1. Cache Tier Health in Affected Region - Highest signal**\nOwner: Cache / SRE. Timebox: 5 min.\n* Are all cache nodes members? Any node down/evicted at 14:05?\n* Memory pressure, evictions/sec, OOMs, CPU, client connections, network errors.\n* Client-side errors from API to cache, not just server-side.\n\n**2. Hot Endpoint Traffic Analysis**\nOwner: Backend. Timebox: 5 min.\n* Traces for `/v1/<hot-endpoint>`: QPS change at 14:05? Tenant / customer spike?\n* Cache key pattern: Hit rate per key, new keys per second, payload size. Is there a single hot key or key explosion?\n* Origin latency without cache - is the DB / downstream for this endpoint spiking?\n\n**3. Regional Serving Health**\nOwner: SRE\n* Pod/instance count, crash loops, autoscaler events, CPU throttling/GC for API fleet in that region at 14:05.\n* Service mesh / load balancer latency between API and cache tier.\n* Breakdown of that 0.8% errors: 504 vs 500 vs 429?\n\n### Safe, Reversible Mitigation Proposed\n\nGoal: Protect customers while preserving evidence.\n\n**Proposal: Traffic shed + regional shift:**\n1.  Put a **temporal rate limit / load shed** on `GET /v1/<hot-endpoint>` in affected region only - e.g., 50% of normal limit, return 429 with Retry-After. Protects other endpoints.\n2.  **Shift 50% of API traffic out of affected region** to healthy regions via Global LB weight change. 1-click reversible.\n3.  Enable request coalescing + stale-while-revalidate for that endpoint if feature-flag exists.\n\n**Why safe:** No data loss, no restarts, instantly reversible via config/LB. Directly addresses symptom - cache miss storm - without touching DB/cache.\n\n**Rollback:** Revert LB weight + remove rate limit.\n\nNext update in 20 minutes or upon completion of check #1.\n\nPlease attach cache and endpoint dashboards in thread.",
          "annotations": [],
          "logprobs": []
        }
      ],
      "status": "completed"
    }
  ],
  "status": "completed",
  "usage": {
    "input_tokens": 146,
    "output_tokens": 2500,
    "total_tokens": 2646,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens_details": {
      "reasoning_tokens": 1714
    }
  },
  "top_logprobs": 0,
  "temperature": 1,
  "top_p": 1,
  "service_tier": "auto",
  "reasoning": {
    "effort": "high"
  },
  "background": false,
  "tools": [],
  "tool_choice": "auto",
  "truncation": "disabled",
  "completed_at": 1791471639,
  "error": null,
  "incomplete_details": null,
  "instructions": null,
  "parallel_tool_calls": true,
  "store": true,
  "max_output_tokens": 4096
}