bedrock-converse-cache.recorded.test.ts 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import { describe, expect } from "bun:test"
  2. import { Effect } from "effect"
  3. import { CacheHint, LLM } from "../../src"
  4. import { LLMClient } from "../../src/route"
  5. import { AmazonBedrock } from "../../src/providers"
  6. import { LARGE_CACHEABLE_SYSTEM } from "../recorded-scenarios"
  7. import { recordedTests } from "../recorded-test"
  8. const RECORDING_REGION = process.env.BEDROCK_RECORDING_REGION ?? "us-east-1"
  9. // Use a Claude model on Bedrock — Nova has automatic prefix caching that
  10. // doesn't reliably surface `cacheRead`/`cacheWrite` in usage, so the second
  11. // call wouldn't deterministically prove cache mapping works. Override with
  12. // BEDROCK_CACHE_MODEL_ID if your account has access elsewhere.
  13. const model = AmazonBedrock.configure({
  14. credentials: {
  15. region: RECORDING_REGION,
  16. accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "fixture",
  17. secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "fixture",
  18. sessionToken: process.env.AWS_SESSION_TOKEN,
  19. },
  20. }).model(process.env.BEDROCK_CACHE_MODEL_ID ?? "us.anthropic.claude-haiku-4-5-20251001-v1:0")
  21. const cacheRequest = LLM.request({
  22. id: "recorded_bedrock_cache",
  23. model,
  24. system: [{ type: "text", text: LARGE_CACHEABLE_SYSTEM, cache: new CacheHint({ type: "ephemeral" }) }],
  25. prompt: "Say hi.",
  26. // Manual hint on the system part is the only marker we want here — skip the
  27. // auto-policy's latest-user-message breakpoint so the cassette body matches.
  28. cache: "none",
  29. generation: { maxTokens: 16, temperature: 0 },
  30. })
  31. const recorded = recordedTests({
  32. prefix: "bedrock-converse-cache",
  33. provider: "amazon-bedrock",
  34. protocol: "bedrock-converse",
  35. requires: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
  36. // Two identical requests in one cassette — replay walks the cassette in
  37. // recording order so the second call replays the cached-hit interaction.
  38. })
  39. describe("Bedrock Converse cache recorded", () => {
  40. recorded.effect.with("writes then reads cachePoint on identical second call", { tags: ["cache"] }, () =>
  41. Effect.gen(function* () {
  42. const first = yield* LLMClient.generate(cacheRequest)
  43. expect(first.usage?.cacheReadInputTokens ?? 0).toBeGreaterThanOrEqual(0)
  44. const second = yield* LLMClient.generate(cacheRequest)
  45. expect(second.usage?.cacheReadInputTokens ?? 0).toBeGreaterThan(0)
  46. }),
  47. )
  48. })