provider-github-copilot.test.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { describe, expect } from "bun:test"
  2. import { Effect } from "effect"
  3. import { ModelV2 } from "@opencode-ai/core/model"
  4. import { PluginV2 } from "@opencode-ai/core/plugin"
  5. import { GithubCopilotPlugin } from "@opencode-ai/core/plugin/provider/github-copilot"
  6. import { fakeSelectorSdk, it, model } from "../v2/plugin/provider-helper"
  7. describe("GithubCopilotPlugin", () => {
  8. it.effect("creates the bundled Copilot SDK for the GitHub Copilot package", () =>
  9. Effect.gen(function* () {
  10. const plugin = yield* PluginV2.Service
  11. yield* plugin.add(GithubCopilotPlugin)
  12. const ignored = yield* plugin.trigger(
  13. "aisdk.sdk",
  14. {
  15. model: model("github-copilot", "gpt-5"),
  16. package: "@ai-sdk/openai-compatible",
  17. options: { name: "github-copilot" },
  18. },
  19. {},
  20. )
  21. const result = yield* plugin.trigger(
  22. "aisdk.sdk",
  23. {
  24. model: model("github-copilot", "gpt-5"),
  25. package: "@ai-sdk/github-copilot",
  26. options: { name: "github-copilot" },
  27. },
  28. {},
  29. )
  30. expect(ignored.sdk).toBeUndefined()
  31. expect(result.sdk).toBeDefined()
  32. }),
  33. )
  34. it.effect("selects languageModel when responses and chat are absent", () =>
  35. Effect.gen(function* () {
  36. const plugin = yield* PluginV2.Service
  37. const calls: string[] = []
  38. yield* plugin.add(GithubCopilotPlugin)
  39. yield* plugin.trigger(
  40. "aisdk.language",
  41. {
  42. model: model("github-copilot", "claude-sonnet-4"),
  43. sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
  44. options: {},
  45. },
  46. {},
  47. )
  48. expect(calls).toEqual(["languageModel:claude-sonnet-4"])
  49. }),
  50. )
  51. it.effect("selects languageModel with the API model ID when responses and chat are absent", () =>
  52. Effect.gen(function* () {
  53. const plugin = yield* PluginV2.Service
  54. const calls: string[] = []
  55. yield* plugin.add(GithubCopilotPlugin)
  56. yield* plugin.trigger(
  57. "aisdk.language",
  58. {
  59. model: model("github-copilot", "alias", { apiID: ModelV2.ID.make("claude-sonnet-4") }),
  60. sdk: { languageModel: fakeSelectorSdk(calls).languageModel },
  61. options: {},
  62. },
  63. {},
  64. )
  65. expect(calls).toEqual(["languageModel:claude-sonnet-4"])
  66. }),
  67. )
  68. it.effect("uses responses for gpt-5 models except gpt-5-mini", () =>
  69. Effect.gen(function* () {
  70. const plugin = yield* PluginV2.Service
  71. const calls: string[] = []
  72. yield* plugin.add(GithubCopilotPlugin)
  73. yield* plugin.trigger(
  74. "aisdk.language",
  75. { model: model("github-copilot", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} },
  76. {},
  77. )
  78. yield* plugin.trigger(
  79. "aisdk.language",
  80. { model: model("github-copilot", "gpt-5.1-codex"), sdk: fakeSelectorSdk(calls), options: {} },
  81. {},
  82. )
  83. yield* plugin.trigger(
  84. "aisdk.language",
  85. { model: model("github-copilot", "gpt-4o"), sdk: fakeSelectorSdk(calls), options: {} },
  86. {},
  87. )
  88. yield* plugin.trigger(
  89. "aisdk.language",
  90. { model: model("github-copilot", "gpt-5-mini"), sdk: fakeSelectorSdk(calls), options: {} },
  91. {},
  92. )
  93. yield* plugin.trigger(
  94. "aisdk.language",
  95. { model: model("github-copilot", "gpt-5-mini-2025-08-07"), sdk: fakeSelectorSdk(calls), options: {} },
  96. {},
  97. )
  98. expect(calls).toEqual([
  99. "responses:gpt-5",
  100. "responses:gpt-5.1-codex",
  101. "chat:gpt-4o",
  102. "chat:gpt-5-mini",
  103. "chat:gpt-5-mini-2025-08-07",
  104. ])
  105. }),
  106. )
  107. it.effect("uses the API model ID when selecting responses or chat", () =>
  108. Effect.gen(function* () {
  109. const plugin = yield* PluginV2.Service
  110. const calls: string[] = []
  111. yield* plugin.add(GithubCopilotPlugin)
  112. yield* plugin.trigger(
  113. "aisdk.language",
  114. {
  115. model: model("github-copilot", "default", { apiID: ModelV2.ID.make("gpt-5") }),
  116. sdk: fakeSelectorSdk(calls),
  117. options: {},
  118. },
  119. {},
  120. )
  121. yield* plugin.trigger(
  122. "aisdk.language",
  123. {
  124. model: model("github-copilot", "small", { apiID: ModelV2.ID.make("gpt-5-mini") }),
  125. sdk: fakeSelectorSdk(calls),
  126. options: {},
  127. },
  128. {},
  129. )
  130. yield* plugin.trigger(
  131. "aisdk.language",
  132. {
  133. model: model("github-copilot", "sonnet", { apiID: ModelV2.ID.make("claude-sonnet-4") }),
  134. sdk: fakeSelectorSdk(calls),
  135. options: {},
  136. },
  137. {},
  138. )
  139. expect(calls).toEqual(["responses:gpt-5", "chat:gpt-5-mini", "chat:claude-sonnet-4"])
  140. }),
  141. )
  142. it.effect("filters gpt-5-chat-latest before Copilot language selection", () =>
  143. Effect.gen(function* () {
  144. const plugin = yield* PluginV2.Service
  145. yield* plugin.add(GithubCopilotPlugin)
  146. const result = yield* plugin.trigger(
  147. "model.update",
  148. {},
  149. { model: model("github-copilot", "gpt-5-chat-latest"), cancel: false },
  150. )
  151. expect(result.cancel).toBe(true)
  152. }),
  153. )
  154. it.effect("does not filter gpt-5-chat-latest for non-Copilot providers", () =>
  155. Effect.gen(function* () {
  156. const plugin = yield* PluginV2.Service
  157. yield* plugin.add(GithubCopilotPlugin)
  158. const result = yield* plugin.trigger(
  159. "model.update",
  160. {},
  161. { model: model("custom-copilot", "gpt-5-chat-latest"), cancel: false },
  162. )
  163. expect(result.cancel).toBe(false)
  164. }),
  165. )
  166. it.effect("ignores non-Copilot providers", () =>
  167. Effect.gen(function* () {
  168. const plugin = yield* PluginV2.Service
  169. const calls: string[] = []
  170. yield* plugin.add(GithubCopilotPlugin)
  171. const result = yield* plugin.trigger(
  172. "aisdk.language",
  173. { model: model("openai", "gpt-5"), sdk: fakeSelectorSdk(calls), options: {} },
  174. {},
  175. )
  176. expect(calls).toEqual([])
  177. expect(result.language).toBeUndefined()
  178. }),
  179. )
  180. })