mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-15 13:55:19 +00:00
Merge branch 'dev' into feat/otel-logger-handler
This commit is contained in:
commit
49926f06ee
8 changed files with 80 additions and 27 deletions
|
|
@ -644,12 +644,19 @@ AUDIT_EXCLUDED_PATHS = [path.lstrip("/") for path in AUDIT_EXCLUDED_PATHS]
|
||||||
ENABLE_OTEL = os.environ.get("ENABLE_OTEL", "False").lower() == "true"
|
ENABLE_OTEL = os.environ.get("ENABLE_OTEL", "False").lower() == "true"
|
||||||
ENABLE_OTEL_METRICS = os.environ.get("ENABLE_OTEL_METRICS", "False").lower() == "true"
|
ENABLE_OTEL_METRICS = os.environ.get("ENABLE_OTEL_METRICS", "False").lower() == "true"
|
||||||
ENABLE_OTEL_LOGS = os.environ.get("ENABLE_OTEL_LOGS", "False").lower() == "true"
|
ENABLE_OTEL_LOGS = os.environ.get("ENABLE_OTEL_LOGS", "False").lower() == "true"
|
||||||
|
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get(
|
OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get(
|
||||||
"OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"
|
"OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"
|
||||||
)
|
)
|
||||||
|
OTEL_METRICS_EXPORTER_OTLP_ENDPOINT = os.environ.get(
|
||||||
|
"OTEL_METRICS_EXPORTER_OTLP_ENDPOINT", OTEL_EXPORTER_OTLP_ENDPOINT
|
||||||
|
)
|
||||||
OTEL_EXPORTER_OTLP_INSECURE = (
|
OTEL_EXPORTER_OTLP_INSECURE = (
|
||||||
os.environ.get("OTEL_EXPORTER_OTLP_INSECURE", "False").lower() == "true"
|
os.environ.get("OTEL_EXPORTER_OTLP_INSECURE", "False").lower() == "true"
|
||||||
)
|
)
|
||||||
|
OTEL_METRICS_EXPORTER_OTLP_INSECURE = (
|
||||||
|
os.environ.get("OTEL_METRICS_EXPORTER_OTLP_INSECURE", "False").lower() == "true"
|
||||||
|
)
|
||||||
OTEL_SERVICE_NAME = os.environ.get("OTEL_SERVICE_NAME", "open-webui")
|
OTEL_SERVICE_NAME = os.environ.get("OTEL_SERVICE_NAME", "open-webui")
|
||||||
OTEL_RESOURCE_ATTRIBUTES = os.environ.get(
|
OTEL_RESOURCE_ATTRIBUTES = os.environ.get(
|
||||||
"OTEL_RESOURCE_ATTRIBUTES", ""
|
"OTEL_RESOURCE_ATTRIBUTES", ""
|
||||||
|
|
@ -660,11 +667,21 @@ OTEL_TRACES_SAMPLER = os.environ.get(
|
||||||
OTEL_BASIC_AUTH_USERNAME = os.environ.get("OTEL_BASIC_AUTH_USERNAME", "")
|
OTEL_BASIC_AUTH_USERNAME = os.environ.get("OTEL_BASIC_AUTH_USERNAME", "")
|
||||||
OTEL_BASIC_AUTH_PASSWORD = os.environ.get("OTEL_BASIC_AUTH_PASSWORD", "")
|
OTEL_BASIC_AUTH_PASSWORD = os.environ.get("OTEL_BASIC_AUTH_PASSWORD", "")
|
||||||
|
|
||||||
|
OTEL_METRICS_BASIC_AUTH_USERNAME = os.environ.get(
|
||||||
|
"OTEL_METRICS_BASIC_AUTH_USERNAME", OTEL_BASIC_AUTH_USERNAME
|
||||||
|
)
|
||||||
|
OTEL_METRICS_BASIC_AUTH_PASSWORD = os.environ.get(
|
||||||
|
"OTEL_METRICS_BASIC_AUTH_PASSWORD", OTEL_BASIC_AUTH_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
OTEL_OTLP_SPAN_EXPORTER = os.environ.get(
|
OTEL_OTLP_SPAN_EXPORTER = os.environ.get(
|
||||||
"OTEL_OTLP_SPAN_EXPORTER", "grpc"
|
"OTEL_OTLP_SPAN_EXPORTER", "grpc"
|
||||||
).lower() # grpc or http
|
).lower() # grpc or http
|
||||||
|
|
||||||
|
OTEL_METRICS_OTLP_SPAN_EXPORTER = os.environ.get(
|
||||||
|
"OTEL_METRICS_OTLP_SPAN_EXPORTER", OTEL_OTLP_SPAN_EXPORTER
|
||||||
|
).lower() # grpc or http
|
||||||
|
|
||||||
|
|
||||||
####################################
|
####################################
|
||||||
# TOOLS/FUNCTIONS PIP OPTIONS
|
# TOOLS/FUNCTIONS PIP OPTIONS
|
||||||
|
|
|
||||||
|
|
@ -19,37 +19,69 @@ from __future__ import annotations
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from typing import Dict, List, Sequence, Any
|
from typing import Dict, List, Sequence, Any
|
||||||
|
from base64 import b64encode
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from opentelemetry import metrics
|
from opentelemetry import metrics
|
||||||
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
|
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
|
||||||
OTLPMetricExporter,
|
OTLPMetricExporter,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from opentelemetry.exporter.otlp.proto.http.metric_exporter import (
|
||||||
|
OTLPMetricExporter as OTLPHttpMetricExporter,
|
||||||
|
)
|
||||||
from opentelemetry.sdk.metrics import MeterProvider
|
from opentelemetry.sdk.metrics import MeterProvider
|
||||||
from opentelemetry.sdk.metrics.view import View
|
from opentelemetry.sdk.metrics.view import View
|
||||||
from opentelemetry.sdk.metrics.export import (
|
from opentelemetry.sdk.metrics.export import (
|
||||||
PeriodicExportingMetricReader,
|
PeriodicExportingMetricReader,
|
||||||
)
|
)
|
||||||
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
from opentelemetry.sdk.resources import Resource
|
||||||
|
|
||||||
from open_webui.env import OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT
|
|
||||||
|
|
||||||
|
from open_webui.env import (
|
||||||
|
OTEL_SERVICE_NAME,
|
||||||
|
OTEL_METRICS_EXPORTER_OTLP_ENDPOINT,
|
||||||
|
OTEL_METRICS_BASIC_AUTH_USERNAME,
|
||||||
|
OTEL_METRICS_BASIC_AUTH_PASSWORD,
|
||||||
|
OTEL_METRICS_OTLP_SPAN_EXPORTER,
|
||||||
|
OTEL_METRICS_EXPORTER_OTLP_INSECURE,
|
||||||
|
)
|
||||||
from open_webui.socket.main import get_active_user_ids
|
from open_webui.socket.main import get_active_user_ids
|
||||||
from open_webui.models.users import Users
|
from open_webui.models.users import Users
|
||||||
|
|
||||||
_EXPORT_INTERVAL_MILLIS = 10_000 # 10 seconds
|
_EXPORT_INTERVAL_MILLIS = 10_000 # 10 seconds
|
||||||
|
|
||||||
|
|
||||||
def _build_meter_provider() -> MeterProvider:
|
def _build_meter_provider(resource: Resource) -> MeterProvider:
|
||||||
"""Return a configured MeterProvider."""
|
"""Return a configured MeterProvider."""
|
||||||
|
headers = []
|
||||||
|
if OTEL_METRICS_BASIC_AUTH_USERNAME and OTEL_METRICS_BASIC_AUTH_PASSWORD:
|
||||||
|
auth_string = (
|
||||||
|
f"{OTEL_METRICS_BASIC_AUTH_USERNAME}:{OTEL_METRICS_BASIC_AUTH_PASSWORD}"
|
||||||
|
)
|
||||||
|
auth_header = b64encode(auth_string.encode()).decode()
|
||||||
|
headers = [("authorization", f"Basic {auth_header}")]
|
||||||
|
|
||||||
# Periodic reader pushes metrics over OTLP/gRPC to collector
|
# Periodic reader pushes metrics over OTLP/gRPC to collector
|
||||||
readers: List[PeriodicExportingMetricReader] = [
|
if OTEL_METRICS_OTLP_SPAN_EXPORTER == "http":
|
||||||
PeriodicExportingMetricReader(
|
readers: List[PeriodicExportingMetricReader] = [
|
||||||
OTLPMetricExporter(endpoint=OTEL_EXPORTER_OTLP_ENDPOINT),
|
PeriodicExportingMetricReader(
|
||||||
export_interval_millis=_EXPORT_INTERVAL_MILLIS,
|
OTLPHttpMetricExporter(
|
||||||
)
|
endpoint=OTEL_METRICS_EXPORTER_OTLP_ENDPOINT, headers=headers
|
||||||
]
|
),
|
||||||
|
export_interval_millis=_EXPORT_INTERVAL_MILLIS,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
readers: List[PeriodicExportingMetricReader] = [
|
||||||
|
PeriodicExportingMetricReader(
|
||||||
|
OTLPMetricExporter(
|
||||||
|
endpoint=OTEL_METRICS_EXPORTER_OTLP_ENDPOINT,
|
||||||
|
insecure=OTEL_METRICS_EXPORTER_OTLP_INSECURE,
|
||||||
|
headers=headers,
|
||||||
|
),
|
||||||
|
export_interval_millis=_EXPORT_INTERVAL_MILLIS,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
# Optional view to limit cardinality: drop user-agent etc.
|
# Optional view to limit cardinality: drop user-agent etc.
|
||||||
views: List[View] = [
|
views: List[View] = [
|
||||||
|
|
@ -70,17 +102,17 @@ def _build_meter_provider() -> MeterProvider:
|
||||||
]
|
]
|
||||||
|
|
||||||
provider = MeterProvider(
|
provider = MeterProvider(
|
||||||
resource=Resource.create({SERVICE_NAME: OTEL_SERVICE_NAME}),
|
resource=resource,
|
||||||
metric_readers=list(readers),
|
metric_readers=list(readers),
|
||||||
views=views,
|
views=views,
|
||||||
)
|
)
|
||||||
return provider
|
return provider
|
||||||
|
|
||||||
|
|
||||||
def setup_metrics(app: FastAPI) -> None:
|
def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||||
"""Attach OTel metrics middleware to *app* and initialise provider."""
|
"""Attach OTel metrics middleware to *app* and initialise provider."""
|
||||||
|
|
||||||
metrics.set_meter_provider(_build_meter_provider())
|
metrics.set_meter_provider(_build_meter_provider(resource))
|
||||||
meter = metrics.get_meter(__name__)
|
meter = metrics.get_meter(__name__)
|
||||||
|
|
||||||
# Instruments
|
# Instruments
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,8 @@ from open_webui.env import (
|
||||||
|
|
||||||
def setup(app: FastAPI, db_engine: Engine):
|
def setup(app: FastAPI, db_engine: Engine):
|
||||||
# set up trace
|
# set up trace
|
||||||
trace.set_tracer_provider(
|
resource = Resource.create(attributes={SERVICE_NAME: OTEL_SERVICE_NAME})
|
||||||
TracerProvider(
|
trace.set_tracer_provider(TracerProvider(resource=resource))
|
||||||
resource=Resource.create(attributes={SERVICE_NAME: OTEL_SERVICE_NAME})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add basic auth header only if both username and password are not empty
|
# Add basic auth header only if both username and password are not empty
|
||||||
headers = []
|
headers = []
|
||||||
|
|
@ -56,4 +53,4 @@ def setup(app: FastAPI, db_engine: Engine):
|
||||||
|
|
||||||
# set up metrics only if enabled
|
# set up metrics only if enabled
|
||||||
if ENABLE_OTEL_METRICS:
|
if ENABLE_OTEL_METRICS:
|
||||||
setup_metrics(app)
|
setup_metrics(app, resource)
|
||||||
|
|
|
||||||
|
|
@ -520,6 +520,8 @@ async def get_tool_servers_data(
|
||||||
openapi_data = response.get("openapi", {})
|
openapi_data = response.get("openapi", {})
|
||||||
|
|
||||||
if info and isinstance(openapi_data, dict):
|
if info and isinstance(openapi_data, dict):
|
||||||
|
openapi_data["info"] = openapi_data.get("info", {})
|
||||||
|
|
||||||
if "name" in info:
|
if "name" in info:
|
||||||
openapi_data["info"]["title"] = info.get("name", "Tool Server")
|
openapi_data["info"]["title"] = info.get("name", "Tool Server")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ passlib[bcrypt]==1.7.4
|
||||||
cryptography
|
cryptography
|
||||||
|
|
||||||
requests==2.32.4
|
requests==2.32.4
|
||||||
aiohttp==3.11.11
|
aiohttp==3.12.15
|
||||||
async-timeout
|
async-timeout
|
||||||
aiocache
|
aiocache
|
||||||
aiofiles
|
aiofiles
|
||||||
|
|
@ -42,14 +42,14 @@ asgiref==3.8.1
|
||||||
# AI libraries
|
# AI libraries
|
||||||
openai
|
openai
|
||||||
anthropic
|
anthropic
|
||||||
google-genai==1.15.0
|
google-genai==1.28.0
|
||||||
google-generativeai==0.8.5
|
google-generativeai==0.8.5
|
||||||
tiktoken
|
tiktoken
|
||||||
|
|
||||||
langchain==0.3.26
|
langchain==0.3.26
|
||||||
langchain-community==0.3.26
|
langchain-community==0.3.26
|
||||||
|
|
||||||
fake-useragent==2.1.0
|
fake-useragent==2.2.0
|
||||||
chromadb==0.6.3
|
chromadb==0.6.3
|
||||||
posthog==5.4.0
|
posthog==5.4.0
|
||||||
pymilvus==2.5.0
|
pymilvus==2.5.0
|
||||||
|
|
@ -75,7 +75,7 @@ docx2txt==0.8
|
||||||
python-pptx==1.0.2
|
python-pptx==1.0.2
|
||||||
unstructured==0.16.17
|
unstructured==0.16.17
|
||||||
nltk==3.9.1
|
nltk==3.9.1
|
||||||
Markdown==3.7
|
Markdown==3.8.2
|
||||||
pypandoc==1.15
|
pypandoc==1.15
|
||||||
pandas==2.2.3
|
pandas==2.2.3
|
||||||
openpyxl==3.1.5
|
openpyxl==3.1.5
|
||||||
|
|
@ -97,7 +97,7 @@ onnxruntime==1.20.1
|
||||||
faster-whisper==1.1.1
|
faster-whisper==1.1.1
|
||||||
|
|
||||||
PyJWT[crypto]==2.10.1
|
PyJWT[crypto]==2.10.1
|
||||||
authlib==1.4.1
|
authlib==1.6.1
|
||||||
|
|
||||||
black==25.1.0
|
black==25.1.0
|
||||||
langfuse==2.44.0
|
langfuse==2.44.0
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,11 @@
|
||||||
{:else if token.text.includes(`<source_id`)}
|
{:else if token.text.includes(`<source_id`)}
|
||||||
<Source {id} {token} onClick={onSourceClick} />
|
<Source {id} {token} onClick={onSourceClick} />
|
||||||
{:else}
|
{:else}
|
||||||
{token.text}
|
{@const br = token.text.match(/<br\s*\/?>/)}
|
||||||
|
{#if br}
|
||||||
|
<br />
|
||||||
|
{:else}
|
||||||
|
{token.text}
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
|
|
||||||
const initPinnedModelsSortable = () => {
|
const initPinnedModelsSortable = () => {
|
||||||
const pinnedModelsList = document.getElementById('pinned-models-list');
|
const pinnedModelsList = document.getElementById('pinned-models-list');
|
||||||
if (pinnedModelsList) {
|
if (pinnedModelsList && !$mobile) {
|
||||||
new Sortable(pinnedModelsList, {
|
new Sortable(pinnedModelsList, {
|
||||||
animation: 150,
|
animation: 150,
|
||||||
onUpdate: async (event) => {
|
onUpdate: async (event) => {
|
||||||
|
|
|
||||||
|
|
@ -1072,7 +1072,7 @@
|
||||||
"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "به خود به عنوان \"کاربر\" اشاره کنید (مثلاً، \"کاربر در حال یادگیری اسپانیایی است\")",
|
"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "به خود به عنوان \"کاربر\" اشاره کنید (مثلاً، \"کاربر در حال یادگیری اسپانیایی است\")",
|
||||||
"References from": "مراجع از",
|
"References from": "مراجع از",
|
||||||
"Refused when it shouldn't have": "رد شده زمانی که باید نباشد",
|
"Refused when it shouldn't have": "رد شده زمانی که باید نباشد",
|
||||||
"Regenerate": "ری\u200cسازی",
|
"Regenerate": "تولید مجدد",
|
||||||
"Reindex": "فهرست\u200cبندی مجدد",
|
"Reindex": "فهرست\u200cبندی مجدد",
|
||||||
"Reindex Knowledge Base Vectors": "فهرست\u200cبندی مجدد بردارهای پایگاه دانش",
|
"Reindex Knowledge Base Vectors": "فهرست\u200cبندی مجدد بردارهای پایگاه دانش",
|
||||||
"Release Notes": "یادداشت\u200cهای انتشار",
|
"Release Notes": "یادداشت\u200cهای انتشار",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue