bedrock-mantle.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { describe, expect } from "bun:test"
  2. import { Effect } from "effect"
  3. import { HttpClientRequest } from "effect/unstable/http"
  4. import { LLM } from "../../src/index.js"
  5. import { AmazonBedrockMantle } from "../../src/providers.js"
  6. import { compileRequest, LLMClient } from "../../src/route/client.js"
  7. import { it } from "../lib/effect.js"
  8. import { dynamicResponse } from "../lib/http.js"
  9. import { recordedTests } from "../recorded-test.js"
  10. const credentials = {
  11. region: "us-east-2",
  12. accessKeyId: "AKIAIOSFODNN7EXAMPLE",
  13. secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  14. }
  15. describe("Amazon Bedrock Mantle provider", () => {
  16. it.effect("uses Chat by default and exposes Responses", () =>
  17. Effect.gen(function* () {
  18. const provider = AmazonBedrockMantle.configure({ credentials })
  19. const chat = yield* compileRequest(LLM.request({ model: provider.model("openai.gpt-oss-120b"), prompt: "Hi" }))
  20. const responses = yield* compileRequest(
  21. LLM.request({ model: provider.responses("openai.gpt-oss-120b"), prompt: "Hi" }),
  22. )
  23. expect(chat).toMatchObject({
  24. route: "bedrock-mantle-chat",
  25. protocol: "openai-chat",
  26. body: { model: "openai.gpt-oss-120b" },
  27. })
  28. expect(responses).toMatchObject({
  29. route: "bedrock-mantle-responses",
  30. protocol: "openai-responses",
  31. body: { model: "openai.gpt-oss-120b", store: false },
  32. })
  33. }),
  34. )
  35. it.effect("uses the Mantle endpoint and signing service", () =>
  36. Effect.gen(function* () {
  37. const seen: Array<{ readonly url: string; readonly authorization: string | undefined }> = []
  38. const model = AmazonBedrockMantle.configure({ credentials, region: "us-west-1" }).responses("openai.gpt-oss-120b")
  39. yield* LLMClient.generate(LLM.request({ model, prompt: "Hi" })).pipe(
  40. Effect.provide(
  41. dynamicResponse((input) =>
  42. Effect.gen(function* () {
  43. const request = yield* HttpClientRequest.toWeb(input.request)
  44. seen.push({ url: request.url, authorization: request.headers.get("authorization") ?? undefined })
  45. return input.respond("", { headers: { "content-type": "text/event-stream" } })
  46. }),
  47. ),
  48. ),
  49. Effect.flip,
  50. )
  51. expect(seen[0]?.url).toBe("https://bedrock-mantle.us-west-1.api.aws/v1/responses")
  52. expect(seen[0]?.authorization).toContain("/us-west-1/bedrock-mantle/aws4_request")
  53. }),
  54. )
  55. it.effect("supports bearer authentication and custom base URLs", () =>
  56. Effect.gen(function* () {
  57. const seen: Array<{ readonly url: string; readonly authorization: string | undefined }> = []
  58. const model = AmazonBedrockMantle.configure({
  59. apiKey: "test-key",
  60. baseURL: "https://mantle.test/v1",
  61. }).chat("openai.gpt-oss-safeguard-20b")
  62. yield* LLMClient.generate(LLM.request({ model, prompt: "Hi" })).pipe(
  63. Effect.provide(
  64. dynamicResponse((input) =>
  65. Effect.gen(function* () {
  66. const request = yield* HttpClientRequest.toWeb(input.request)
  67. seen.push({ url: request.url, authorization: request.headers.get("authorization") ?? undefined })
  68. return input.respond("", { headers: { "content-type": "text/event-stream" } })
  69. }),
  70. ),
  71. ),
  72. Effect.flip,
  73. )
  74. expect(seen).toEqual([{ url: "https://mantle.test/v1/chat/completions", authorization: "Bearer test-key" }])
  75. }),
  76. )
  77. })
  78. const recorded = recordedTests({
  79. prefix: "bedrock-mantle",
  80. provider: "amazon-bedrock",
  81. protocol: "openai-responses",
  82. requires: ["AWS_BEARER_TOKEN_BEDROCK"],
  83. metadata: { model: "openai.gpt-oss-120b" },
  84. })
  85. describe("Amazon Bedrock Mantle recorded", () => {
  86. recorded.effect("streams text", () =>
  87. Effect.gen(function* () {
  88. const response = yield* LLMClient.generate(
  89. LLM.request({
  90. model: AmazonBedrockMantle.configure({
  91. apiKey: process.env.AWS_BEARER_TOKEN_BEDROCK ?? "fixture",
  92. region: "us-east-1",
  93. }).responses("openai.gpt-oss-120b"),
  94. prompt: "Reply with exactly: hello",
  95. generation: { maxTokens: 256, temperature: 0 },
  96. }),
  97. )
  98. expect(response.text.trim().toLowerCase()).toBe("hello")
  99. }),
  100. )
  101. })