digitalocean.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { expect } from "bun:test"
  2. import { LayerNode } from "@kirincode-ai/core/effect/layer-node"
  3. import { Provider } from "../../src/provider/provider"
  4. import { Effect } from "effect"
  5. import { testEffect } from "../lib/effect"
  6. import { ProviderV2 } from "@kirincode-ai/core/provider"
  7. const DIGITALOCEAN = ProviderV2.ID.make("digitalocean")
  8. const it = testEffect(LayerNode.compile(Provider.node))
  9. const withEnv = <A, E, R>(values: Record<string, string>, effect: Effect.Effect<A, E, R>) =>
  10. Effect.acquireUseRelease(
  11. Effect.sync(() => {
  12. const previous = Object.fromEntries(Object.keys(values).map((key) => [key, process.env[key]] as const))
  13. Object.assign(process.env, values)
  14. return previous
  15. }),
  16. () => effect,
  17. (previous) =>
  18. Effect.sync(() => {
  19. for (const [key, value] of Object.entries(previous)) {
  20. if (value === undefined) delete process.env[key]
  21. else process.env[key] = value
  22. }
  23. }),
  24. )
  25. const withAuth = <A, E, R>(metadata: Record<string, string> | undefined, effect: Effect.Effect<A, E, R>) =>
  26. withEnv(
  27. {
  28. KIRINCODE_AUTH_CONTENT: JSON.stringify({
  29. digitalocean: {
  30. type: "api",
  31. key: "sk_do_test",
  32. ...(metadata ? { metadata } : {}),
  33. },
  34. }),
  35. },
  36. effect,
  37. )
  38. it.instance(
  39. "digitalocean provider autoloads from DIGITALOCEAN_ACCESS_TOKEN",
  40. () =>
  41. withEnv(
  42. { DIGITALOCEAN_ACCESS_TOKEN: "test-token" },
  43. Effect.gen(function* () {
  44. const provider = yield* Provider.Service
  45. const providers = yield* provider.list()
  46. expect(providers[DIGITALOCEAN]).toBeDefined()
  47. expect(providers[DIGITALOCEAN].source).toBe("env")
  48. const baseModel = Object.values(providers[DIGITALOCEAN].models)[0]
  49. expect(baseModel.api.url).toBe("https://inference.do-ai.run/v1")
  50. expect(baseModel.api.npm).toBe("@ai-sdk/openai-compatible")
  51. const routerEntries = Object.keys(providers[DIGITALOCEAN].models).filter((id) => id.startsWith("router:"))
  52. expect(routerEntries.length).toBe(0)
  53. }),
  54. ),
  55. { config: {} },
  56. )
  57. it.instance(
  58. "digitalocean provider.models surfaces cached routers from auth metadata",
  59. () =>
  60. withAuth(
  61. {
  62. routers: JSON.stringify([
  63. { name: "my-router", uuid: "11f1499a-aaaa-bbbb-cccc-4e013e2ddde4" },
  64. { name: "other-router", uuid: "22f1499a-aaaa-bbbb-cccc-4e013e2ddde4" },
  65. ]),
  66. routers_fetched_at: String(Date.now()),
  67. oauth_access: "doo_v1_test",
  68. oauth_expires: String(Date.now() + 60 * 60 * 1000),
  69. },
  70. Effect.gen(function* () {
  71. const provider = yield* Provider.Service
  72. const providers = yield* provider.list()
  73. const models = providers[DIGITALOCEAN].models
  74. expect(models["router:my-router"]).toBeDefined()
  75. expect(models["router:my-router"].api.id).toBe("router:my-router")
  76. expect(models["router:my-router"].api.url).toBe("https://inference.do-ai.run/v1")
  77. expect(models["router:my-router"].api.npm).toBe("@ai-sdk/openai-compatible")
  78. expect(models["router:other-router"]).toBeDefined()
  79. }),
  80. ),
  81. { config: {} },
  82. )
  83. it.instance(
  84. "digitalocean provider.models skips refresh when oauth bearer is expired",
  85. () =>
  86. withAuth(
  87. {
  88. routers: JSON.stringify([{ name: "stale-router", uuid: "stale" }]),
  89. routers_fetched_at: "0",
  90. oauth_access: "doo_v1_expired",
  91. oauth_expires: "1",
  92. },
  93. Effect.gen(function* () {
  94. const provider = yield* Provider.Service
  95. const providers = yield* provider.list()
  96. const models = providers[DIGITALOCEAN].models
  97. expect(models["router:stale-router"]).toBeDefined()
  98. }),
  99. ),
  100. { config: {} },
  101. )
  102. it.instance(
  103. "digitalocean provider.models passes through base models when no auth metadata",
  104. () =>
  105. withEnv(
  106. { DIGITALOCEAN_ACCESS_TOKEN: "test-token" },
  107. Effect.gen(function* () {
  108. const provider = yield* Provider.Service
  109. const providers = yield* provider.list()
  110. const models = providers[DIGITALOCEAN].models
  111. expect(Object.keys(models).length).toBeGreaterThan(0)
  112. expect(Object.keys(models).filter((id) => id.startsWith("router:")).length).toBe(0)
  113. }),
  114. ),
  115. { config: {} },
  116. )