websearch.test.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { describe, expect, test } from "bun:test"
  2. import { Effect } from "effect"
  3. import { parseResponse } from "../../src/tool/mcp-websearch"
  4. import { selectWebSearchProvider, webSearchModelName, webSearchProviderLabel } from "../../src/tool/websearch"
  5. import { webSearchEnabled } from "../../src/tool/registry"
  6. import { it } from "../lib/effect"
  7. import { ProviderV2 } from "@kirincode-ai/core/provider"
  8. const SESSION_ID = "ses_0196aabbccddeeff001122334455"
  9. describe("websearch provider", () => {
  10. test("selects a stable provider per session", () => {
  11. expect(selectWebSearchProvider(SESSION_ID)).toBe(selectWebSearchProvider(SESSION_ID))
  12. })
  13. test("supports an operational override", () => {
  14. const original = process.env.KIRINCODE_WEBSEARCH_PROVIDER
  15. try {
  16. process.env.KIRINCODE_WEBSEARCH_PROVIDER = "parallel"
  17. expect(selectWebSearchProvider(SESSION_ID)).toBe("parallel")
  18. process.env.KIRINCODE_WEBSEARCH_PROVIDER = "exa"
  19. expect(selectWebSearchProvider(SESSION_ID)).toBe("exa")
  20. } finally {
  21. if (original === undefined) delete process.env.KIRINCODE_WEBSEARCH_PROVIDER
  22. else process.env.KIRINCODE_WEBSEARCH_PROVIDER = original
  23. }
  24. })
  25. test("routes to Exa when the Exa flag is enabled", () => {
  26. expect(selectWebSearchProvider(SESSION_ID, { exa: true, parallel: false })).toBe("exa")
  27. })
  28. test("routes to Parallel when the Parallel flag is enabled", () => {
  29. expect(selectWebSearchProvider(SESSION_ID, { exa: false, parallel: true })).toBe("parallel")
  30. })
  31. test("is only enabled for kirincode or explicit websearch provider flags", () => {
  32. expect(webSearchEnabled(ProviderV2.ID.kirincode, { exa: false, parallel: false })).toBe(true)
  33. expect(webSearchEnabled(ProviderV2.ID.openai, { exa: false, parallel: false })).toBe(false)
  34. expect(webSearchEnabled(ProviderV2.ID.openai, { exa: true, parallel: false })).toBe(true)
  35. expect(webSearchEnabled(ProviderV2.ID.openai, { exa: false, parallel: true })).toBe(true)
  36. })
  37. test("uses branded labels", () => {
  38. expect(webSearchProviderLabel("parallel")).toBe("Parallel Web Search")
  39. expect(webSearchProviderLabel("exa")).toBe("Exa Web Search")
  40. expect(webSearchProviderLabel(undefined)).toBe("Web Search")
  41. })
  42. test("uses the provider API model id for Parallel analytics", () => {
  43. expect(
  44. webSearchModelName({
  45. model: {
  46. id: "claude-opus-4-7",
  47. api: { id: "claude-opus-4.7" },
  48. },
  49. }),
  50. ).toBe("claude-opus-4.7")
  51. })
  52. })
  53. describe("websearch MCP response parser", () => {
  54. const payload = JSON.stringify({
  55. jsonrpc: "2.0",
  56. id: 1,
  57. result: {
  58. content: [
  59. {
  60. type: "text",
  61. text: "search results",
  62. },
  63. ],
  64. },
  65. })
  66. it.effect("parses plain JSON-RPC responses", () =>
  67. Effect.gen(function* () {
  68. const result = yield* parseResponse(payload)
  69. expect(result).toBe("search results")
  70. }),
  71. )
  72. it.effect("parses SSE JSON-RPC responses", () =>
  73. Effect.gen(function* () {
  74. const result = yield* parseResponse(`event: message\ndata: ${payload}\n\n`)
  75. expect(result).toBe("search results")
  76. }),
  77. )
  78. it.effect("ignores non-JSON SSE data frames", () =>
  79. Effect.gen(function* () {
  80. const result = yield* parseResponse(`data: [DONE]\ndata: ${payload}\n\n`)
  81. expect(result).toBe("search results")
  82. }),
  83. )
  84. })