feat: replaced ollama with litellm (#18)
Some checks failed
CI / Format (push) Successful in 3s
CI / Clippy (push) Successful in 2m53s
CI / Security Audit (push) Successful in 1m42s
CI / Tests (push) Failing after 3m59s
CI / Deploy (push) Has been skipped
CI / E2E Tests (push) Has been skipped

Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com>
Reviewed-on: #18
This commit was merged in pull request #18.
This commit is contained in:
2026-02-26 17:52:47 +00:00
parent 0deaaca848
commit fe4f8e84ae
28 changed files with 1107 additions and 500 deletions

View File

@@ -58,15 +58,15 @@
"title": "Dashboard",
"subtitle": "KI-Nachrichten und Neuigkeiten",
"topic_placeholder": "Themenname...",
"ollama_settings": "Ollama-Einstellungen",
"settings_hint": "Leer lassen, um OLLAMA_URL / OLLAMA_MODEL aus .env zu verwenden",
"ollama_url": "Ollama-URL",
"ollama_url_placeholder": "Verwendet OLLAMA_URL aus .env",
"litellm_settings": "LiteLLM-Einstellungen",
"settings_hint": "Leer lassen, um LITELLM_URL / LITELLM_MODEL aus .env zu verwenden",
"litellm_url": "LiteLLM-URL",
"litellm_url_placeholder": "Verwendet LITELLM_URL aus .env",
"model": "Modell",
"model_placeholder": "Verwendet OLLAMA_MODEL aus .env",
"model_placeholder": "Verwendet LITELLM_MODEL aus .env",
"searching": "Suche laeuft...",
"search_failed": "Suche fehlgeschlagen: {e}",
"ollama_status": "Ollama-Status",
"litellm_status": "LiteLLM-Status",
"trending": "Im Trend",
"recent_searches": "Letzte Suchen"
},
@@ -144,6 +144,16 @@
"email_address": "E-Mail-Adresse",
"email_placeholder": "kollege@firma.de",
"send_invite": "Einladung senden",
"total_spend": "Gesamtausgaben",
"total_tokens": "Tokens gesamt",
"model_usage": "Nutzung nach Modell",
"model": "Modell",
"tokens": "Tokens",
"spend": "Ausgaben",
"usage_unavailable": "Nutzungsdaten nicht verfuegbar",
"loading_usage": "Nutzungsdaten werden geladen...",
"prompt_tokens": "Prompt-Tokens",
"completion_tokens": "Antwort-Tokens",
"pricing_title": "Preise",
"pricing_subtitle": "Waehlen Sie den passenden Plan fuer Ihre Organisation"
},

View File

@@ -58,15 +58,15 @@
"title": "Dashboard",
"subtitle": "AI news and updates",
"topic_placeholder": "Topic name...",
"ollama_settings": "Ollama Settings",
"settings_hint": "Leave empty to use OLLAMA_URL / OLLAMA_MODEL from .env",
"ollama_url": "Ollama URL",
"ollama_url_placeholder": "Uses OLLAMA_URL from .env",
"litellm_settings": "LiteLLM Settings",
"settings_hint": "Leave empty to use LITELLM_URL / LITELLM_MODEL from .env",
"litellm_url": "LiteLLM URL",
"litellm_url_placeholder": "Uses LITELLM_URL from .env",
"model": "Model",
"model_placeholder": "Uses OLLAMA_MODEL from .env",
"model_placeholder": "Uses LITELLM_MODEL from .env",
"searching": "Searching...",
"search_failed": "Search failed: {e}",
"ollama_status": "Ollama Status",
"litellm_status": "LiteLLM Status",
"trending": "Trending",
"recent_searches": "Recent Searches"
},
@@ -144,6 +144,16 @@
"email_address": "Email Address",
"email_placeholder": "colleague@company.com",
"send_invite": "Send Invite",
"total_spend": "Total Spend",
"total_tokens": "Total Tokens",
"model_usage": "Usage by Model",
"model": "Model",
"tokens": "Tokens",
"spend": "Spend",
"usage_unavailable": "Usage data unavailable",
"loading_usage": "Loading usage data...",
"prompt_tokens": "Prompt Tokens",
"completion_tokens": "Completion Tokens",
"pricing_title": "Pricing",
"pricing_subtitle": "Choose the plan that fits your organization"
},

View File

@@ -58,15 +58,15 @@
"title": "Panel de control",
"subtitle": "Noticias y actualizaciones de IA",
"topic_placeholder": "Nombre del tema...",
"ollama_settings": "Configuracion de Ollama",
"settings_hint": "Dejar vacio para usar OLLAMA_URL / OLLAMA_MODEL del archivo .env",
"ollama_url": "URL de Ollama",
"ollama_url_placeholder": "Usa OLLAMA_URL del archivo .env",
"litellm_settings": "Configuracion de LiteLLM",
"settings_hint": "Dejar vacio para usar LITELLM_URL / LITELLM_MODEL del archivo .env",
"litellm_url": "URL de LiteLLM",
"litellm_url_placeholder": "Usa LITELLM_URL del archivo .env",
"model": "Modelo",
"model_placeholder": "Usa OLLAMA_MODEL del archivo .env",
"model_placeholder": "Usa LITELLM_MODEL del archivo .env",
"searching": "Buscando...",
"search_failed": "La busqueda fallo: {e}",
"ollama_status": "Estado de Ollama",
"litellm_status": "Estado de LiteLLM",
"trending": "Tendencias",
"recent_searches": "Busquedas recientes"
},
@@ -144,6 +144,16 @@
"email_address": "Direccion de correo electronico",
"email_placeholder": "colega@empresa.com",
"send_invite": "Enviar invitacion",
"total_spend": "Gasto total",
"total_tokens": "Tokens totales",
"model_usage": "Uso por modelo",
"model": "Modelo",
"tokens": "Tokens",
"spend": "Gasto",
"usage_unavailable": "Datos de uso no disponibles",
"loading_usage": "Cargando datos de uso...",
"prompt_tokens": "Tokens de entrada",
"completion_tokens": "Tokens de respuesta",
"pricing_title": "Precios",
"pricing_subtitle": "Elija el plan que se adapte a su organizacion"
},

View File

@@ -58,15 +58,15 @@
"title": "Tableau de bord",
"subtitle": "Actualites et mises a jour IA",
"topic_placeholder": "Nom du sujet...",
"ollama_settings": "Parametres Ollama",
"settings_hint": "Laissez vide pour utiliser OLLAMA_URL / OLLAMA_MODEL du fichier .env",
"ollama_url": "URL Ollama",
"ollama_url_placeholder": "Utilise OLLAMA_URL du fichier .env",
"litellm_settings": "Parametres LiteLLM",
"settings_hint": "Laissez vide pour utiliser LITELLM_URL / LITELLM_MODEL du fichier .env",
"litellm_url": "URL LiteLLM",
"litellm_url_placeholder": "Utilise LITELLM_URL du fichier .env",
"model": "Modele",
"model_placeholder": "Utilise OLLAMA_MODEL du fichier .env",
"model_placeholder": "Utilise LITELLM_MODEL du fichier .env",
"searching": "Recherche en cours...",
"search_failed": "Echec de la recherche : {e}",
"ollama_status": "Statut Ollama",
"litellm_status": "Statut LiteLLM",
"trending": "Tendances",
"recent_searches": "Recherches recentes"
},
@@ -144,6 +144,16 @@
"email_address": "Adresse e-mail",
"email_placeholder": "collegue@entreprise.com",
"send_invite": "Envoyer l'invitation",
"total_spend": "Depenses totales",
"total_tokens": "Tokens totaux",
"model_usage": "Utilisation par modele",
"model": "Modele",
"tokens": "Tokens",
"spend": "Depenses",
"usage_unavailable": "Donnees d'utilisation indisponibles",
"loading_usage": "Chargement des donnees d'utilisation...",
"prompt_tokens": "Tokens d'entree",
"completion_tokens": "Tokens de reponse",
"pricing_title": "Tarifs",
"pricing_subtitle": "Choisissez le plan adapte a votre organisation"
},

View File

@@ -58,15 +58,15 @@
"title": "Painel",
"subtitle": "Noticias e atualizacoes de IA",
"topic_placeholder": "Nome do topico...",
"ollama_settings": "Definicoes do Ollama",
"settings_hint": "Deixe vazio para usar OLLAMA_URL / OLLAMA_MODEL do .env",
"ollama_url": "URL do Ollama",
"ollama_url_placeholder": "Utiliza OLLAMA_URL do .env",
"litellm_settings": "Definicoes do LiteLLM",
"settings_hint": "Deixe vazio para usar LITELLM_URL / LITELLM_MODEL do .env",
"litellm_url": "URL do LiteLLM",
"litellm_url_placeholder": "Utiliza LITELLM_URL do .env",
"model": "Modelo",
"model_placeholder": "Utiliza OLLAMA_MODEL do .env",
"model_placeholder": "Utiliza LITELLM_MODEL do .env",
"searching": "A pesquisar...",
"search_failed": "A pesquisa falhou: {e}",
"ollama_status": "Estado do Ollama",
"litellm_status": "Estado do LiteLLM",
"trending": "Em destaque",
"recent_searches": "Pesquisas recentes"
},
@@ -144,6 +144,16 @@
"email_address": "Endereco de Email",
"email_placeholder": "colleague@company.com",
"send_invite": "Enviar Convite",
"total_spend": "Gasto total",
"total_tokens": "Tokens totais",
"model_usage": "Uso por modelo",
"model": "Modelo",
"tokens": "Tokens",
"spend": "Gasto",
"usage_unavailable": "Dados de uso indisponiveis",
"loading_usage": "Carregando dados de uso...",
"prompt_tokens": "Tokens de entrada",
"completion_tokens": "Tokens de resposta",
"pricing_title": "Precos",
"pricing_subtitle": "Escolha o plano adequado a sua organizacao"
},

View File

@@ -1,4 +1,4 @@
/*! tailwindcss v4.2.0 | MIT License | https://tailwindcss.com */
/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
@@ -162,59 +162,6 @@
}
}
@layer utilities {
.diff {
@layer daisyui.l1.l2.l3 {
position: relative;
display: grid;
width: 100%;
overflow: hidden;
webkit-user-select: none;
user-select: none;
grid-template-rows: 1fr 1.8rem 1fr;
direction: ltr;
container-type: inline-size;
grid-template-columns: auto 1fr;
&:focus-visible, &:has(.diff-item-1:focus-visible) {
outline-style: var(--tw-outline-style);
outline-width: 2px;
outline-offset: 1px;
outline-color: var(--color-base-content);
}
&:focus-visible {
outline-style: var(--tw-outline-style);
outline-width: 2px;
outline-offset: 1px;
outline-color: var(--color-base-content);
.diff-resizer {
min-width: 95cqi;
max-width: 95cqi;
}
}
&:has(.diff-item-1:focus-visible) {
outline-style: var(--tw-outline-style);
outline-width: 2px;
outline-offset: 1px;
.diff-resizer {
min-width: 5cqi;
max-width: 5cqi;
}
}
@supports (-webkit-overflow-scrolling: touch) and (overflow: -webkit-paged-x) {
&:focus {
.diff-resizer {
min-width: 5cqi;
max-width: 5cqi;
}
}
&:has(.diff-item-1:focus) {
.diff-resizer {
min-width: 95cqi;
max-width: 95cqi;
}
}
}
}
}
.modal {
@layer daisyui.l1.l2.l3 {
pointer-events: none;
@@ -1110,31 +1057,98 @@
}
}
}
.chat-bubble {
.range {
@layer daisyui.l1.l2.l3 {
position: relative;
display: block;
width: fit-content;
border-radius: var(--radius-field);
background-color: var(--color-base-300);
padding-inline: calc(0.25rem * 4);
padding-block: calc(0.25rem * 2);
color: var(--color-base-content);
grid-row-end: 3;
min-height: 2rem;
min-width: 2.5rem;
max-width: 90%;
&:before {
position: absolute;
bottom: calc(0.25rem * 0);
height: calc(0.25rem * 3);
width: calc(0.25rem * 3);
background-color: inherit;
content: "";
mask-repeat: no-repeat;
mask-image: var(--mask-chat);
mask-position: 0px -1px;
mask-size: 0.8125rem;
appearance: none;
webkit-appearance: none;
--range-thumb: var(--color-base-100);
--range-thumb-size: calc(var(--size-selector, 0.25rem) * 6);
--range-progress: currentColor;
--range-fill: 1;
--range-p: 0.25rem;
--range-bg: currentColor;
@supports (color: color-mix(in lab, red, red)) {
--range-bg: color-mix(in oklab, currentColor 10%, #0000);
}
cursor: pointer;
overflow: hidden;
background-color: transparent;
vertical-align: middle;
width: clamp(3rem, 20rem, 100%);
--radius-selector-max: calc(
var(--radius-selector) + var(--radius-selector) + var(--radius-selector)
);
border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max)));
border: none;
height: var(--range-thumb-size);
[dir="rtl"] & {
--range-dir: -1;
}
&:focus {
outline: none;
}
&:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}
&::-webkit-slider-runnable-track {
width: 100%;
background-color: var(--range-bg);
border-radius: var(--radius-selector);
height: calc(var(--range-thumb-size) * 0.5);
}
@media (forced-colors: active) {
&::-webkit-slider-runnable-track {
border: 1px solid;
}
}
@media (forced-colors: active) {
&::-moz-range-track {
border: 1px solid;
}
}
&::-webkit-slider-thumb {
position: relative;
box-sizing: border-box;
border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max)));
background-color: var(--range-thumb);
height: var(--range-thumb-size);
width: var(--range-thumb-size);
border: var(--range-p) solid;
appearance: none;
webkit-appearance: none;
top: 50%;
color: var(--range-progress);
transform: translateY(-50%);
box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, calc((var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2)) 0 0 calc(100cqw * var(--range-fill));
@supports (color: color-mix(in lab, red, red)) {
box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, calc((var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2)) 0 0 calc(100cqw * var(--range-fill));
}
}
&::-moz-range-track {
width: 100%;
background-color: var(--range-bg);
border-radius: var(--radius-selector);
height: calc(var(--range-thumb-size) * 0.5);
}
&::-moz-range-thumb {
position: relative;
box-sizing: border-box;
border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max)));
background-color: currentColor;
height: var(--range-thumb-size);
width: var(--range-thumb-size);
border: var(--range-p) solid;
top: 50%;
color: var(--range-progress);
box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, calc((var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2)) 0 0 calc(100cqw * var(--range-fill));
@supports (color: color-mix(in lab, red, red)) {
box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, calc((var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2)) 0 0 calc(100cqw * var(--range-fill));
}
}
&:disabled {
cursor: not-allowed;
opacity: 30%;
}
}
}
@@ -1525,81 +1539,6 @@
padding: calc(0.25rem * 4);
}
}
.textarea {
@layer daisyui.l1.l2.l3 {
border: var(--border) solid #0000;
min-height: calc(0.25rem * 20);
flex-shrink: 1;
appearance: none;
border-radius: var(--radius-field);
background-color: var(--color-base-100);
padding-block: calc(0.25rem * 2);
vertical-align: middle;
width: clamp(3rem, 20rem, 100%);
padding-inline-start: 0.75rem;
padding-inline-end: 0.75rem;
font-size: max(var(--font-size, 0.875rem), 0.875rem);
touch-action: manipulation;
border-color: var(--input-color);
box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset;
@supports (color: color-mix(in lab, red, red)) {
box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset;
}
--input-color: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
--input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000);
}
textarea {
appearance: none;
background-color: transparent;
border: none;
&:focus, &:focus-within {
--tw-outline-style: none;
outline-style: none;
@media (forced-colors: active) {
outline: 2px solid transparent;
outline-offset: 2px;
}
}
}
&:focus, &:focus-within {
--input-color: var(--color-base-content);
box-shadow: 0 1px var(--input-color);
@supports (color: color-mix(in lab, red, red)) {
box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000);
}
outline: 2px solid var(--input-color);
outline-offset: 2px;
isolation: isolate;
}
@media (pointer: coarse) {
@supports (-webkit-touch-callout: none) {
&:focus, &:focus-within {
--font-size: 1rem;
}
}
}
&:has(> textarea[disabled]), &:is(:disabled, [disabled]) {
cursor: not-allowed;
border-color: var(--color-base-200);
background-color: var(--color-base-200);
color: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, var(--color-base-content) 40%, transparent);
}
&::placeholder {
color: var(--color-base-content);
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, var(--color-base-content) 20%, transparent);
}
}
box-shadow: none;
}
&:has(> textarea[disabled]) > textarea[disabled] {
cursor: not-allowed;
}
}
}
.stack {
@layer daisyui.l1.l2.l3 {
display: inline-grid;