> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ramalama.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Supported Models

> Browse models available through the RamaLama Cloud gateway.

export const getSortValue = value => {
  return typeof value === "string" ? value.toLowerCase() : "";
};

export const formatContext = modelInfo => {
  if (!modelInfo) {
    return "N/A";
  }
  const hasInput = modelInfo.max_input_tokens !== null && modelInfo.max_input_tokens !== undefined;
  const hasOutput = modelInfo.max_output_tokens !== null && modelInfo.max_output_tokens !== undefined;
  if (hasInput || hasOutput) {
    const input = hasInput ? modelInfo.max_input_tokens : "N/A";
    const output = hasOutput ? modelInfo.max_output_tokens : "N/A";
    return `${input} in / ${output} out`;
  }
  if (modelInfo.max_tokens) {
    return `${modelInfo.max_tokens} total`;
  }
  return "N/A";
};

export const formatCost = value => {
  if (typeof value !== "number") {
    return "N/A";
  }
  return `$${value.toFixed(6)}/token`;
};

export const resolveApiBaseUrl = override => {
  const normalizedOverride = normalizeBaseUrl(override);
  if (normalizedOverride) {
    return normalizedOverride;
  }
  if (typeof window !== "undefined") {
    const host = window.location ? window.location.hostname : "";
    if (host === "localhost" || host === "127.0.0.1") {
      return "https://api.dev.ramalama.com";
    }
  }
  return "https://api.ramalama.com";
};

export const normalizeBaseUrl = value => {
  if (!value || typeof value !== "string") {
    return "";
  }
  return value.replace(/\/$/, "");
};

export const SupportedModels = ({baseUrl}) => {
  const ReactRef = typeof React !== "undefined" ? React : null;
  if (!ReactRef) {
    return <p className="text-red-600 dark:text-red-400">
        Unable to render the models list. Please try again later.
      </p>;
  }
  const getRowKey = (model, index) => {
    const infoId = model && model.model_info && typeof model.model_info.id === "string" ? model.model_info.id : "";
    if (infoId) {
      return infoId;
    }
    return `model-${index}`;
  };
  const [state, setState] = ReactRef.useState({
    status: "loading",
    models: [],
    error: ""
  });
  const [query, setQuery] = ReactRef.useState("");
  const [page, setPage] = ReactRef.useState(1);
  const pageSize = 10;
  ReactRef.useEffect(() => {
    const getCachedModels = () => {
      if (typeof window === "undefined") {
        return null;
      }
      const cache = window.__RAMALAMA_SUPPORTED_MODELS_CACHE;
      if (cache && Array.isArray(cache.models)) {
        return cache.models;
      }
      return null;
    };
    const setCachedModels = models => {
      if (typeof window === "undefined") {
        return;
      }
      window.__RAMALAMA_SUPPORTED_MODELS_CACHE = {
        models,
        cachedAt: Date.now()
      };
    };
    const cachedModels = getCachedModels();
    if (cachedModels) {
      setState({
        status: "success",
        models: cachedModels,
        error: ""
      });
      return () => {};
    }
    const resolvedBaseUrl = resolveApiBaseUrl(baseUrl);
    const apiUrl = `${resolvedBaseUrl}/auth/api/v1/gateway/models`;
    let isActive = true;
    fetch(apiUrl, {
      credentials: "include"
    }).then(response => {
      if (!response.ok) {
        throw new Error(`Request failed with ${response.status}`);
      }
      return response.json();
    }).then(payload => {
      if (!isActive) {
        return;
      }
      const models = payload && Array.isArray(payload.data) ? payload.data : [];
      setCachedModels(models);
      setState({
        status: "success",
        models,
        error: ""
      });
    }).catch(error => {
      if (!isActive) {
        return;
      }
      setState({
        status: "error",
        models: [],
        error: error && error.message ? error.message : "Unable to load models."
      });
    });
    return () => {
      isActive = false;
    };
  }, [baseUrl]);
  const sortedModels = ReactRef.useMemo(() => {
    if (!state.models.length) {
      return [];
    }
    return state.models.slice().sort((left, right) => {
      const provider = getSortValue(left.provider).localeCompare(getSortValue(right.provider));
      if (provider !== 0) {
        return provider;
      }
      return getSortValue(left.model_short_name).localeCompare(getSortValue(right.model_short_name));
    });
  }, [state.models]);
  const filteredModels = ReactRef.useMemo(() => {
    const trimmed = query.trim().toLowerCase();
    if (!trimmed) {
      return sortedModels;
    }
    return sortedModels.filter(model => {
      const fields = [model.model_short_name, model.model_name, model.provider, model.mode];
      return fields.some(field => {
        return typeof field === "string" && field.toLowerCase().indexOf(trimmed) !== -1;
      });
    });
  }, [sortedModels, query]);
  const totalPages = Math.max(1, Math.ceil(filteredModels.length / pageSize));
  const safePage = page > totalPages ? totalPages : page;
  const pagedModels = ReactRef.useMemo(() => {
    const startIndex = (safePage - 1) * pageSize;
    return filteredModels.slice(startIndex, startIndex + pageSize);
  }, [filteredModels, safePage]);
  ReactRef.useEffect(() => {
    if (page > totalPages) {
      setPage(totalPages);
    }
  }, [page, totalPages]);
  if (state.status === "loading") {
    return <p className="text-gray-500 dark:text-zinc-500">Loading models...</p>;
  }
  if (state.status === "error") {
    return <p className="text-red-600 dark:text-red-400">
        {state.error} Please try again later.
      </p>;
  }
  if (!sortedModels.length) {
    return <p className="text-gray-500 dark:text-zinc-500">
        No models are currently listed for the gateway.
      </p>;
  }
  return <div className="space-y-4">
      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <div className="text-sm text-gray-500 dark:text-zinc-500">
          Showing {filteredModels.length} of {sortedModels.length} models
        </div>
        <input type="search" value={query} onChange={event => {
    setQuery(event.target.value);
    setPage(1);
  }} placeholder="Search models" className="w-full sm:w-64 rounded-md border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 shadow-sm focus:border-blue-500 focus:outline-none dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100" aria-label="Search models" />
      </div>

      <div className="overflow-x-auto">
        <table className="min-w-full text-left text-sm">
          <thead className="border-b border-gray-200 dark:border-zinc-800">
            <tr>
              <th className="py-2 pr-4 font-semibold">Model</th>
              <th className="py-2 pr-4 font-semibold">Provider</th>
              <th className="py-2 pr-4 font-semibold">Mode</th>
              <th className="py-2 pr-4 font-semibold">Context</th>
              <th className="py-2 pr-4 font-semibold">Input Cost</th>
              <th className="py-2 font-semibold">Output Cost</th>
            </tr>
          </thead>
          <tbody>
            {filteredModels.length === 0 ? <tr>
                <td className="py-6 text-sm text-gray-500 dark:text-zinc-500" colSpan="6">
                  No models match your search.
                </td>
              </tr> : pagedModels.map((model, index) => {
    const rowKey = getRowKey(model, index);
    return <tr key={rowKey} className="border-b border-gray-100 dark:border-zinc-900">
                    <td className="py-3 pr-4">
                      <div className="font-medium text-gray-900 dark:text-zinc-50">
                        {model.model_short_name}
                      </div>
                      <div className="text-xs text-gray-500 dark:text-zinc-500">
                        {model.model_name}
                      </div>
                    </td>
                    <td className="py-3 pr-4">{model.provider}</td>
                    <td className="py-3 pr-4">{model.mode ? model.mode : "N/A"}</td>
                    <td className="py-3 pr-4">{formatContext(model.model_info)}</td>
                    <td className="py-3 pr-4">
                      {formatCost(model.model_info ? model.model_info.input_cost_per_token : null)}
                    </td>
                    <td className="py-3">
                      {formatCost(model.model_info ? model.model_info.output_cost_per_token : null)}
                    </td>
                  </tr>;
  })}
          </tbody>
        </table>
      </div>

      <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
        <div className="text-sm text-gray-500 dark:text-zinc-500">
          Page {safePage} of {totalPages}
        </div>
        <div className="flex gap-2">
          <button type="button" className="inline-flex items-center rounded-md border border-gray-200 px-3 py-1.5 text-sm text-gray-700 shadow-sm transition hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40 dark:border-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-900" onClick={() => setPage(safePage - 1)} disabled={safePage <= 1}>
            Previous
          </button>
          <button type="button" className="inline-flex items-center rounded-md border border-gray-200 px-3 py-1.5 text-sm text-gray-700 shadow-sm transition hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40 dark:border-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-900" onClick={() => setPage(safePage + 1)} disabled={safePage >= totalPages}>
            Next
          </button>
        </div>
      </div>
    </div>;
};

<Note>
  To get started with a specific model, use the full model name (e.g. `openai/gpt-3.5-turbo`) from the table below when constructing an api request.
</Note>

<SupportedModels />
