openRouterModelsHelper.ts 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import axios from "axios";
  2. import path from "path";
  3. import fs from "fs/promises";
  4. // Minimal type for what we need from OpenRouter model info in evals
  5. export interface EvalOpenRouterModelInfo {
  6. id: string;
  7. contextWindow: number;
  8. inputPrice?: number; // Price per million tokens
  9. outputPrice?: number; // Price per million tokens
  10. // Add any other fields if they become necessary for evals
  11. }
  12. function logHelper(isVerbose: boolean, message: string) {
  13. if (isVerbose) {
  14. console.log(`[OpenRouterModelsHelper] ${message}`);
  15. }
  16. }
  17. /**
  18. * Ensures the cache directory exists within evals and returns its path
  19. */
  20. async function ensureEvalCacheDirectoryExists(): Promise<string> {
  21. // Cache directory within evals, e.g., evals/.cache/
  22. const cacheDir = path.join(__dirname, "..", ".cache");
  23. await fs.mkdir(cacheDir, { recursive: true });
  24. return cacheDir;
  25. }
  26. /**
  27. * Fetches, parses, and caches OpenRouter model data.
  28. * Tries to read from a local cache first.
  29. * @param isVerbose Enable verbose logging
  30. * @returns A record of model IDs to their info.
  31. */
  32. export async function loadOpenRouterModelData(isVerbose: boolean = false): Promise<Record<string, EvalOpenRouterModelInfo>> {
  33. const cacheDir = await ensureEvalCacheDirectoryExists();
  34. const cacheFilePath = path.join(cacheDir, "openRouterModels.json");
  35. let models: Record<string, EvalOpenRouterModelInfo> = {};
  36. try {
  37. const stats = await fs.stat(cacheFilePath).catch(() => null);
  38. // Use cache if less than 24 hours old
  39. if (stats && (Date.now() - stats.mtimeMs < 24 * 60 * 60 * 1000)) {
  40. logHelper(isVerbose, "Using cached OpenRouter model data.");
  41. const fileContents = await fs.readFile(cacheFilePath, "utf8");
  42. models = JSON.parse(fileContents);
  43. if (Object.keys(models).length > 0) {
  44. return models;
  45. }
  46. logHelper(isVerbose, "Cache was empty or invalid, fetching fresh data.");
  47. } else if (stats) {
  48. logHelper(isVerbose, "Cached OpenRouter model data is stale, fetching fresh data.");
  49. } else {
  50. logHelper(isVerbose, "No cached OpenRouter model data found, fetching fresh data.");
  51. }
  52. } catch (e) {
  53. logHelper(isVerbose, `Error accessing cache, fetching fresh data: ${e}`);
  54. }
  55. try {
  56. const response = await axios.get("https://openrouter.ai/api/v1/models");
  57. if (response.data?.data) {
  58. const rawModels = response.data.data;
  59. const parsedModels: Record<string, EvalOpenRouterModelInfo> = {};
  60. const parsePrice = (price: any) => price ? parseFloat(price) * 1_000_000 : undefined;
  61. for (const rawModel of rawModels) {
  62. parsedModels[rawModel.id] = {
  63. id: rawModel.id,
  64. contextWindow: rawModel.context_length ?? 0,
  65. inputPrice: parsePrice(rawModel.pricing?.prompt),
  66. outputPrice: parsePrice(rawModel.pricing?.completion),
  67. };
  68. }
  69. await fs.writeFile(cacheFilePath, JSON.stringify(parsedModels, null, 2));
  70. logHelper(isVerbose, `Fetched and cached ${Object.keys(parsedModels).length} OpenRouter models.`);
  71. return parsedModels;
  72. } else {
  73. logHelper(isVerbose, "Invalid response structure from OpenRouter API.");
  74. }
  75. } catch (error) {
  76. logHelper(isVerbose, `Error fetching OpenRouter models: ${error}. Attempting to use stale cache if available.`);
  77. // Attempt to read stale cache as a last resort if fetching failed
  78. try {
  79. const fileContents = await fs.readFile(cacheFilePath, "utf8");
  80. models = JSON.parse(fileContents);
  81. if (Object.keys(models).length > 0) {
  82. logHelper(isVerbose, "Successfully loaded stale cache after fetch failure.");
  83. return models;
  84. }
  85. } catch (cacheError) {
  86. logHelper(isVerbose, `Failed to read stale cache: ${cacheError}. Proceeding without OpenRouter model data.`);
  87. }
  88. }
  89. // Return empty if all attempts fail, so the caller can decide how to handle it
  90. return {};
  91. }