Add Gemini option

This commit is contained in:
Thomas Krijnen
2026-04-03 10:49:36 +02:00
parent 5bcf8685ab
commit 5d748c5b04
5 changed files with 63 additions and 6 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
IfcOpenShell AI Assistant
=========================
A web-based client-side (pyodide + OpenAI, Anthropic, or OpenRouter API) model interrogation and generation API based on: ifcedit, ifcquery and ifcmcp (ifcopenshell-mcp) packaged in a HTML+JS application.
A web-based client-side (pyodide + OpenAI, Anthropic, Gemini, or OpenRouter API) model interrogation and generation API based on: ifcedit, ifcquery and ifcmcp (ifcopenshell-mcp) packaged in a HTML+JS application.
### Setup instructions
+6 -2
View File
@@ -1,6 +1,10 @@
function getChatCompletionsUrl(baseURL) {
const root = (baseURL || "https://api.openai.com/v1").replace(/\/+$/, "");
return `${root}/chat/completions`;
}
export async function chat({ apiKey, model, messages, tools }) {
const res = await fetch("https://api.openai.com/v1/chat/completions", {
export async function chat({ apiKey, baseURL, model, messages, tools }) {
const res = await fetch(getChatCompletionsUrl(baseURL), {
method: "POST",
headers: {
"Content-Type": "application/json",
+6 -2
View File
@@ -1,6 +1,10 @@
function getChatCompletionsUrl(baseURL) {
const root = (baseURL || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
return `${root}/chat/completions`;
}
export async function chat({ apiKey, model, messages, tools }) {
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
export async function chat({ apiKey, baseURL, model, messages, tools }) {
const res = await fetch(getChatCompletionsUrl(baseURL), {
method: "POST",
headers: {
"Content-Type": "application/json",
+44 -1
View File
@@ -8,6 +8,9 @@ const PROVIDERS = {
api: openaiApi,
apiKeyLabel: "OpenAI API key",
apiKeyPlaceholder: "sk-...",
baseUrlLabel: "Base URL",
baseUrlPlaceholder: "https://api.openai.com/v1",
baseUrlDefault: "https://api.openai.com/v1",
models: [
{
value: "gpt-5",
@@ -38,10 +41,35 @@ const PROVIDERS = {
},
],
},
gemini: {
api: openaiApi,
apiKeyLabel: "Gemini API key",
apiKeyPlaceholder: "AIza...",
baseUrlLabel: "Base URL",
baseUrlPlaceholder: "https://generativelanguage.googleapis.com/v1beta/openai/",
baseUrlDefault: "https://generativelanguage.googleapis.com/v1beta/openai/",
models: [
{
value: "gemini-3-flash-preview",
label: "gemini-3-flash-preview"
},
{
value: "gemini-2.5-flash",
label: "gemini-2.5-flash"
},
{
value: "gemini-2.5-pro",
label: "gemini-2.5-pro"
},
],
},
openrouter: {
api: openrouterApi,
apiKeyLabel: "OpenRouter API key",
apiKeyPlaceholder: "sk-or-v1-...",
baseUrlLabel: "Base URL",
baseUrlPlaceholder: "https://openrouter.ai/api/v1",
baseUrlDefault: "https://openrouter.ai/api/v1",
models: [
{
value: "openai/gpt-oss-20b",
@@ -79,6 +107,9 @@ const sendBtn = $("send");
const inputEl = $("input");
const apiKeyEl = $("apiKey");
const apiKeyLabelEl = $("apiKeyLabel");
const baseUrlRowEl = $("baseUrlRow");
const baseUrlLabelEl = $("baseUrlLabel");
const baseUrlEl = $("baseUrl");
const modelEl = $("model");
const providerEl = $("provider");
const ifcFileEl = $("ifcFile");
@@ -89,6 +120,15 @@ function onProviderChange() {
const provider = PROVIDERS[providerEl.value];
apiKeyLabelEl.innerHTML = `${provider.apiKeyLabel}<span class="small">stored in browser memory; only sent to provider servers</span>`;
apiKeyEl.placeholder = provider.apiKeyPlaceholder;
baseUrlRowEl.hidden = !provider.baseUrlDefault;
if (provider.baseUrlDefault) {
baseUrlLabelEl.innerHTML = `${provider.baseUrlLabel}<span class="small">override the API endpoint for OpenAI-compatible providers</span>`;
baseUrlEl.placeholder = provider.baseUrlPlaceholder;
baseUrlEl.value = provider.baseUrlDefault;
} else {
baseUrlEl.value = "";
baseUrlEl.placeholder = "";
}
modelEl.innerHTML = provider.models.map(m => `<option value="${m.value}">${m.label}</option>`).join("");
}
@@ -251,13 +291,16 @@ async function runAgentTurn(userText) {
const apiKey = apiKeyEl.value.trim();
if (!apiKey) throw new Error("Missing API key");
const { chat } = PROVIDERS[providerEl.value].api;
const provider = PROVIDERS[providerEl.value];
const { chat } = provider.api;
const baseURL = provider.baseUrlDefault ? baseUrlEl.value.trim() : undefined;
messages.push({ role: "user", content: userText });
for (let i = 0; i < 64; i++) {
const response = await chat({
apiKey,
baseURL,
model: modelEl.value,
messages: [{ role: "system", content: SYSTEM_INSTRUCTIONS }, ...messages],
tools,
+6
View File
@@ -17,6 +17,7 @@
<select id="provider" style="width: 100%;">
<option value="openai">OpenAI</option>
<option value="anthropic">Anthropic (Claude)</option>
<option value="gemini">Gemini</option>
<option value="openrouter">OpenRouter</option>
</select>
</div>
@@ -31,6 +32,11 @@
<input id="apiKey" type="password" placeholder="sk-..." autocomplete="off" style="width: 100%;" />
</div>
<div class="row" id="baseUrlRow">
<label id="baseUrlLabel">Base URL<span class="small">override the API endpoint for OpenAI-compatible providers</span></label>
<input id="baseUrl" type="text" placeholder="https://api.openai.com/v1" autocomplete="off" style="width: 100%;" />
</div>
<hr />
<div class="row" style="margin-bottom: 10px;">