index.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /**
  2. * OpenCode v2 Plugin: Auto-discover models from OpenAI-compatible endpoints.
  3. *
  4. * Queries GET /v1/models and registers all discovered models under the
  5. * configured provider. If the provider does not exist yet, it is created.
  6. */
  7. import { Plugin, Model } from "@opencode/plugin";
  8. // Configuration
  9. const PROVIDER_ID = "OmniRoute";
  10. const PROVIDER_NAME = "OmniRoute";
  11. const BASE_URL = "http://localhost:20128/v1";
  12. const API_KEY = "sk-e9ac58c0b78cd2bc-f17d3e-f6beea67";
  13. export default Plugin.define({
  14. id: "opencode.openai-discovery",
  15. async setup(ctx) {
  16. try {
  17. const headers: Record<string, string> = { "Content-Type": "application/json" };
  18. if (API_KEY) {
  19. headers["Authorization"] = `Bearer ${API_KEY}`;
  20. }
  21. const res = await fetch(`${BASE_URL}/models`, { headers });
  22. if (!res.ok) {
  23. throw new Error(`HTTP ${res.status} from ${BASE_URL}/models`);
  24. }
  25. const json = await res.json();
  26. const rawModels = (json.data || []) as Array<Record<string, unknown>>;
  27. const models = rawModels.map((m) => {
  28. const modelId = typeof m.id === "string" ? m.id : String(m.id);
  29. const contextLength =
  30. typeof m.context_length === "number" ? m.context_length : 131072;
  31. const maxTokens = typeof m.max_tokens === "number" ? m.max_tokens : 32768;
  32. const inputMods = Array.isArray(m.input_modalities)
  33. ? (m.input_modalities as string[])
  34. : ["text"];
  35. const outputMods = Array.isArray(m.output_modalities)
  36. ? (m.output_modalities as string[])
  37. : ["text"];
  38. return {
  39. id: modelId,
  40. modelID: modelId,
  41. providerID: PROVIDER_ID,
  42. name: modelId,
  43. capabilities: {
  44. tools: true,
  45. input: inputMods,
  46. output: outputMods,
  47. },
  48. variants: [],
  49. cost: [{ input: 0, output: 0, cache: { read: 0, write: 0 } }],
  50. limit: {
  51. context: contextLength,
  52. output: Math.min(maxTokens, contextLength),
  53. },
  54. time: {
  55. released: Math.floor(Date.now() / 1000),
  56. },
  57. status: "active" as const,
  58. enabled: true,
  59. } as unknown as Model.Info;
  60. });
  61. ctx.provider.transform((editor) => {
  62. const source = editor.get(PROVIDER_ID);
  63. if (source) {
  64. // Merge discovered models into the existing provider
  65. const existing = [...source.models.values()];
  66. const existingIDs = new Set(existing.map((model) => String(model.id)));
  67. const merged = [
  68. ...existing,
  69. ...models.filter((m) => !existingIDs.has(String(m.id))),
  70. ];
  71. editor.models.set(PROVIDER_ID, merged);
  72. } else {
  73. // Create the provider with all discovered models
  74. editor.add({
  75. info: {
  76. id: PROVIDER_ID,
  77. name: PROVIDER_NAME,
  78. activation: "enabled" as const,
  79. package: "aisdk:@ai-sdk/openai-compatible",
  80. settings: {
  81. baseURL: BASE_URL,
  82. apiKey: API_KEY,
  83. },
  84. } as never,
  85. models,
  86. });
  87. }
  88. });
  89. console.log(
  90. `[openai-discovery] Registered ${models.length} models in provider "${PROVIDER_ID}"`
  91. );
  92. } catch (error) {
  93. console.error(
  94. `[openai-discovery] Failed to discover models: ${error instanceof Error ? error.message : String(error)}`
  95. );
  96. }
  97. },
  98. });