mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-03-14 12:12:40 +00:00
Compare commits
6 Commits
right-side
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfd168cde9 | ||
|
|
6c7ae243d0 | ||
|
|
c4a2ff2593 | ||
|
|
4b74a6dc76 | ||
|
|
eea5f5b380 | ||
|
|
ae428ba684 |
@@ -957,7 +957,7 @@ ENTERPRISE_EDITION_ENABLED = (
|
||||
#####
|
||||
# Image Generation Configuration (DEPRECATED)
|
||||
# These environment variables will be deprecated soon.
|
||||
# To configure image generation, please visit the Image Generation page in the Admin Settings.
|
||||
# To configure image generation, please visit the Image Generation page in the Admin Panel.
|
||||
#####
|
||||
# Azure Image Configurations
|
||||
AZURE_IMAGE_API_VERSION = os.environ.get("AZURE_IMAGE_API_VERSION") or os.environ.get(
|
||||
|
||||
@@ -81,6 +81,7 @@ from onyx.server.manage.llm.models import VisionProviderResponse
|
||||
from onyx.server.manage.llm.utils import generate_bedrock_display_name
|
||||
from onyx.server.manage.llm.utils import generate_ollama_display_name
|
||||
from onyx.server.manage.llm.utils import infer_vision_support
|
||||
from onyx.server.manage.llm.utils import is_embedding_model
|
||||
from onyx.server.manage.llm.utils import is_reasoning_model
|
||||
from onyx.server.manage.llm.utils import is_valid_bedrock_model
|
||||
from onyx.server.manage.llm.utils import ModelMetadata
|
||||
@@ -1374,6 +1375,10 @@ def get_litellm_available_models(
|
||||
try:
|
||||
model_details = LitellmModelDetails.model_validate(model)
|
||||
|
||||
# Skip embedding models
|
||||
if is_embedding_model(model_details.id):
|
||||
continue
|
||||
|
||||
results.append(
|
||||
LitellmFinalModelResponse(
|
||||
provider_name=model_details.owned_by,
|
||||
|
||||
@@ -366,3 +366,18 @@ def extract_vendor_from_model_name(model_name: str, provider: str) -> str | None
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_embedding_model(model_name: str) -> bool:
|
||||
"""Checks for if a model is an embedding model"""
|
||||
from litellm import get_model_info
|
||||
|
||||
try:
|
||||
# get_model_info raises on unknown models
|
||||
# default to False
|
||||
model_info = get_model_info(model_name)
|
||||
except Exception:
|
||||
return False
|
||||
is_embedding_mode = model_info.get("mode") == "embedding"
|
||||
|
||||
return is_embedding_mode
|
||||
|
||||
@@ -737,7 +737,7 @@ pygithub==2.5.0
|
||||
# via onyx
|
||||
pygments==2.19.2
|
||||
# via rich
|
||||
pyjwt==2.11.0
|
||||
pyjwt==2.12.0
|
||||
# via
|
||||
# fastapi-users
|
||||
# mcp
|
||||
|
||||
@@ -353,7 +353,7 @@ pygments==2.19.2
|
||||
# via
|
||||
# ipython
|
||||
# ipython-pygments-lexers
|
||||
pyjwt==2.11.0
|
||||
pyjwt==2.12.0
|
||||
# via mcp
|
||||
pyparsing==3.2.5
|
||||
# via matplotlib
|
||||
|
||||
@@ -218,7 +218,7 @@ pydantic-core==2.33.2
|
||||
# via pydantic
|
||||
pydantic-settings==2.12.0
|
||||
# via mcp
|
||||
pyjwt==2.11.0
|
||||
pyjwt==2.12.0
|
||||
# via mcp
|
||||
python-dateutil==2.8.2
|
||||
# via
|
||||
|
||||
@@ -308,7 +308,7 @@ pydantic-core==2.33.2
|
||||
# via pydantic
|
||||
pydantic-settings==2.12.0
|
||||
# via mcp
|
||||
pyjwt==2.11.0
|
||||
pyjwt==2.12.0
|
||||
# via mcp
|
||||
python-dateutil==2.8.2
|
||||
# via
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from onyx.server.manage.llm.utils import generate_bedrock_display_name
|
||||
from onyx.server.manage.llm.utils import generate_ollama_display_name
|
||||
from onyx.server.manage.llm.utils import infer_vision_support
|
||||
from onyx.server.manage.llm.utils import is_embedding_model
|
||||
from onyx.server.manage.llm.utils import is_reasoning_model
|
||||
from onyx.server.manage.llm.utils import is_valid_bedrock_model
|
||||
from onyx.server.manage.llm.utils import strip_openrouter_vendor_prefix
|
||||
@@ -209,3 +210,35 @@ class TestIsReasoningModel:
|
||||
is_reasoning_model("anthropic/claude-3-5-sonnet", "Claude 3.5 Sonnet")
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
class TestIsEmbeddingModel:
|
||||
"""Tests for embedding model detection."""
|
||||
|
||||
def test_openai_embedding_ada(self) -> None:
|
||||
assert is_embedding_model("text-embedding-ada-002") is True
|
||||
|
||||
def test_openai_embedding_3_small(self) -> None:
|
||||
assert is_embedding_model("text-embedding-3-small") is True
|
||||
|
||||
def test_openai_embedding_3_large(self) -> None:
|
||||
assert is_embedding_model("text-embedding-3-large") is True
|
||||
|
||||
def test_cohere_embed_model(self) -> None:
|
||||
assert is_embedding_model("embed-english-v3.0") is True
|
||||
|
||||
def test_bedrock_titan_embed(self) -> None:
|
||||
assert is_embedding_model("amazon.titan-embed-text-v1") is True
|
||||
|
||||
def test_gpt4o_not_embedding(self) -> None:
|
||||
assert is_embedding_model("gpt-4o") is False
|
||||
|
||||
def test_gpt4_not_embedding(self) -> None:
|
||||
assert is_embedding_model("gpt-4") is False
|
||||
|
||||
def test_dall_e_not_embedding(self) -> None:
|
||||
assert is_embedding_model("dall-e-3") is False
|
||||
|
||||
def test_unknown_custom_model_not_embedding(self) -> None:
|
||||
"""Custom/local models not in litellm's model DB should default to False."""
|
||||
assert is_embedding_model("my-custom-local-model-v1") is False
|
||||
|
||||
6
uv.lock
generated
6
uv.lock
generated
@@ -5643,11 +5643,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.11.0"
|
||||
version = "2.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a8/10/e8192be5f38f3e8e7e046716de4cae33d56fd5ae08927a823bb916be36c1/pyjwt-2.12.0.tar.gz", hash = "sha256:2f62390b667cd8257de560b850bb5a883102a388829274147f1d724453f8fb02", size = 102511, upload-time = "2026-03-12T17:15:30.831Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/70/70f895f404d363d291dcf62c12c85fdd47619ad9674ac0f53364d035925a/pyjwt-2.12.0-py3-none-any.whl", hash = "sha256:9bb459d1bdd0387967d287f5656bf7ec2b9a26645d1961628cda1764e087fd6e", size = 29700, upload-time = "2026-03-12T17:15:29.257Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
|
||||
20
web/lib/opal/src/icons/curate.tsx
Normal file
20
web/lib/opal/src/icons/curate.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgCurate = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M8 9L8 14.5M8 9C7.35971 8.35971 6.9055 8 6 8H2.5L2.5 13.5H6C6.9055 13.5 7.35971 13.8597 8 14.5M8 9C8.64029 8.35971 9.09449 8 10 8H13.5L13.5 13.5H10C9.09449 13.5 8.64029 13.8597 8 14.5M10.25 3.75C10.25 4.99264 9.24264 6 8 6C6.75736 6 5.75 4.99264 5.75 3.75C5.75 2.50736 6.75736 1.5 8 1.5C9.24264 1.5 10.25 2.50736 10.25 3.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgCurate;
|
||||
@@ -54,6 +54,7 @@ export { default as SvgColumn } from "@opal/icons/column";
|
||||
export { default as SvgCopy } from "@opal/icons/copy";
|
||||
export { default as SvgCornerRightUpDot } from "@opal/icons/corner-right-up-dot";
|
||||
export { default as SvgCpu } from "@opal/icons/cpu";
|
||||
export { default as SvgCurate } from "@opal/icons/curate";
|
||||
export { default as SvgCreditCard } from "@opal/icons/credit-card";
|
||||
export { default as SvgDashboard } from "@opal/icons/dashboard";
|
||||
export { default as SvgDevKit } from "@opal/icons/dev-kit";
|
||||
@@ -135,6 +136,7 @@ export { default as SvgPlayCircle } from "@opal/icons/play-circle";
|
||||
export { default as SvgPlug } from "@opal/icons/plug";
|
||||
export { default as SvgPlus } from "@opal/icons/plus";
|
||||
export { default as SvgPlusCircle } from "@opal/icons/plus-circle";
|
||||
export { default as SvgProgressBars } from "@opal/icons/progress-bars";
|
||||
export { default as SvgProgressCircle } from "@opal/icons/progress-circle";
|
||||
export { default as SvgQuestionMarkSmall } from "@opal/icons/question-mark-small";
|
||||
export { default as SvgQuoteEnd } from "@opal/icons/quote-end";
|
||||
@@ -176,9 +178,16 @@ export { default as SvgTwoLineSmall } from "@opal/icons/two-line-small";
|
||||
export { default as SvgUnplug } from "@opal/icons/unplug";
|
||||
export { default as SvgUploadCloud } from "@opal/icons/upload-cloud";
|
||||
export { default as SvgUser } from "@opal/icons/user";
|
||||
export { default as SvgUserCheck } from "@opal/icons/user-check";
|
||||
export { default as SvgUserEdit } from "@opal/icons/user-edit";
|
||||
export { default as SvgUserKey } from "@opal/icons/user-key";
|
||||
export { default as SvgUserManage } from "@opal/icons/user-manage";
|
||||
export { default as SvgUserMinus } from "@opal/icons/user-minus";
|
||||
export { default as SvgUserPlus } from "@opal/icons/user-plus";
|
||||
export { default as SvgUserShield } from "@opal/icons/user-shield";
|
||||
export { default as SvgUserSpeaker } from "@opal/icons/user-speaker";
|
||||
export { default as SvgUserSync } from "@opal/icons/user-sync";
|
||||
export { default as SvgUserX } from "@opal/icons/user-x";
|
||||
export { default as SvgUsers } from "@opal/icons/users";
|
||||
export { default as SvgVolume } from "@opal/icons/volume";
|
||||
export { default as SvgVolumeOff } from "@opal/icons/volume-off";
|
||||
|
||||
20
web/lib/opal/src/icons/progress-bars.tsx
Normal file
20
web/lib/opal/src/icons/progress-bars.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgProgressBars = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M5.5 2.00003L13.25 2C13.9403 2 14.5 2.55964 14.5 3.25C14.5 3.94036 13.9403 4.5 13.25 4.5L5.5 4.50003M5.5 2.00003L2.74998 2C2.05963 2 1.49998 2.55964 1.49998 3.25C1.49998 3.94036 2.05963 4.5 2.74998 4.5L5.5 4.50003M5.5 2.00003V4.50003M10.5 11.5H13.25C13.9403 11.5 14.5 12.0596 14.5 12.75C14.5 13.4404 13.9403 14 13.25 14H10.5M10.5 11.5H2.74998C2.05963 11.5 1.49998 12.0596 1.49998 12.75C1.49998 13.4404 2.05963 14 2.74999 14H10.5M10.5 11.5V14M8 6.75H13.25C13.9403 6.75 14.5 7.30964 14.5 8C14.5 8.69036 13.9403 9.25 13.25 9.25H8M8 6.75H2.74998C2.05963 6.75 1.49998 7.30964 1.49998 8C1.49998 8.69036 2.05963 9.25 2.74998 9.25H8M8 6.75V9.25"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgProgressBars;
|
||||
20
web/lib/opal/src/icons/user-check.tsx
Normal file
20
web/lib/opal/src/icons/user-check.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserCheck = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11 14C11 13.6667 11 13.3333 11 13C11 11.3431 9.65684 10 7.99998 10H4.00002C2.34316 10 1 11.3431 1 13C1 13.3333 1 13.6667 1 14M10.75 7.49999L12.25 9L15 6.24999M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserCheck;
|
||||
20
web/lib/opal/src/icons/user-edit.tsx
Normal file
20
web/lib/opal/src/icons/user-edit.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserEdit = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M1 14C1 13.6667 1 13.3333 1 13C1 11.3431 2.34316 10 4.00002 10H7M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75ZM12.09 8.41421C12.3552 8.149 12.7149 8 13.09 8C13.2757 8 13.4596 8.03658 13.6312 8.10765C13.8028 8.17872 13.9587 8.28289 14.09 8.41421C14.2213 8.54554 14.3255 8.70144 14.3966 8.87302C14.4676 9.0446 14.5042 9.2285 14.5042 9.41421C14.5042 9.59993 14.4676 9.78383 14.3966 9.95541C14.3255 10.127 14.2213 10.2829 14.09 10.4142L10.6667 13.8333L8 14.5L8.66667 11.8333L12.09 8.41421Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserEdit;
|
||||
20
web/lib/opal/src/icons/user-key.tsx
Normal file
20
web/lib/opal/src/icons/user-key.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserKey = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M1 14C1 13.6667 1 13.3333 1 13C1 11.3431 2.34316 10 4.00002 10H8.5M12.625 10C13.6605 10 14.5 9.16053 14.5 8.125C14.5 7.08947 13.6605 6.25 12.625 6.25C11.5895 6.25 10.75 7.08947 10.75 8.125C10.75 9.16053 11.5895 10 12.625 10ZM12.625 10V12.25M12.625 14.5V13.5M12.625 13.5H13.875V12.25H12.625M12.625 13.5V12.25M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserKey;
|
||||
20
web/lib/opal/src/icons/user-minus.tsx
Normal file
20
web/lib/opal/src/icons/user-minus.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserMinus = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11 14C11 13.6667 11 13.3333 11 13C11 11.3431 9.65684 10 7.99998 10H4.00002C2.34316 10 1 11.3431 1 13C1 13.3333 1 13.6667 1 14M10.75 7.49999L14.75 7.50007M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserMinus;
|
||||
20
web/lib/opal/src/icons/user-shield.tsx
Normal file
20
web/lib/opal/src/icons/user-shield.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserShield = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M1 14C1 13.6667 1 13.3333 1 13C1 11.3431 2.34316 10 4.00002 10H7M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75ZM12 14.5C12 14.5 14.5 13.25 14.5 11.375V9L12 8L9.5 9V11.375C9.5 13.25 12 14.5 12 14.5Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserShield;
|
||||
20
web/lib/opal/src/icons/user-speaker.tsx
Normal file
20
web/lib/opal/src/icons/user-speaker.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserSpeaker = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M1 14C1 13.6667 1 13.3333 1 13C1 11.3431 2.34316 10 4.00002 10H7.99998C9.65684 10 11 11.3431 11 13C11 13.3333 11 13.6667 11 14H14.5V10L12.7071 8.20711M12 7.5L12.7071 8.20711M12.7071 8.20711C13.0976 7.81658 13.0976 7.18342 12.7071 6.79289C12.3166 6.40237 11.6834 6.40237 11.2929 6.79289C10.9024 7.18342 10.9024 7.81658 11.2929 8.20711C11.6834 8.59763 12.3166 8.59763 12.7071 8.20711ZM8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserSpeaker;
|
||||
20
web/lib/opal/src/icons/user-x.tsx
Normal file
20
web/lib/opal/src/icons/user-x.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { IconProps } from "@opal/types";
|
||||
const SvgUserX = ({ size, ...props }: IconProps) => (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M11 14C11 13.6667 11 13.3333 11 13C11 11.3431 9.65684 10 7.99998 10H4.00002C2.34316 10 1 11.3431 1 13C1 13.3333 1 13.6667 1 14M11.5 8.5L13.25 6.75M13.25 6.75L15 5M13.25 6.75L15 8.5M13.25 6.75L11.5 5M8.75 4.75C8.75 6.26878 7.51878 7.5 6 7.5C4.48122 7.5 3.25 6.26878 3.25 4.75C3.25 3.23122 4.48122 2 6 2C7.51878 2 8.75 3.23122 8.75 4.75Z"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgUserX;
|
||||
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -10309,7 +10309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
|
||||
"integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import MCPPageContent from "@/sections/actions/MCPPageContent";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.MCP_ACTIONS]!;
|
||||
const route = ADMIN_ROUTES.MCP_ACTIONS;
|
||||
|
||||
export default function Main() {
|
||||
return (
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import OpenApiPageContent from "@/sections/actions/OpenApiPageContent";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.OPENAPI_ACTIONS]!;
|
||||
const route = ADMIN_ROUTES.OPENAPI_ACTIONS;
|
||||
|
||||
export default function Main() {
|
||||
return (
|
||||
|
||||
@@ -32,7 +32,10 @@ import { SettingsContext } from "@/providers/SettingsProvider";
|
||||
import SourceTile from "@/components/SourceTile";
|
||||
import InputTypeIn from "@/refresh-components/inputs/InputTypeIn";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTES.ADD_CONNECTOR;
|
||||
|
||||
function SourceTileTooltipWrapper({
|
||||
sourceMetadata,
|
||||
preSelect,
|
||||
@@ -124,7 +127,6 @@ function SourceTileTooltipWrapper({
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.ADD_CONNECTOR]!;
|
||||
const sources = useMemo(() => listSourceMetadata(), []);
|
||||
|
||||
const [rawSearchTerm, setSearchTerm] = useState("");
|
||||
|
||||
@@ -11,10 +11,11 @@ import { useAdminPersonas } from "@/hooks/useAdminPersonas";
|
||||
import { Persona } from "./interfaces";
|
||||
import { ThreeDotsLoader } from "@/components/Loading";
|
||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { useState, useEffect } from "react";
|
||||
import Pagination from "@/refresh-components/Pagination";
|
||||
|
||||
const route = ADMIN_ROUTES.AGENTS;
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
function MainContent({
|
||||
@@ -120,7 +121,6 @@ function MainContent({
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.AGENTS]!;
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const { personas, totalItems, isLoading, error, refresh } = useAdminPersonas({
|
||||
pageNum: currentPage - 1, // Backend uses 0-indexed pages
|
||||
|
||||
@@ -32,9 +32,9 @@ import CopyIconButton from "@/refresh-components/buttons/CopyIconButton";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { SvgEdit, SvgKey, SvgRefreshCw } from "@opal/icons";
|
||||
import { useCloudSubscription } from "@/hooks/useCloudSubscription";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.API_KEYS]!;
|
||||
const route = ADMIN_ROUTES.API_KEYS;
|
||||
|
||||
function Main() {
|
||||
const {
|
||||
|
||||
@@ -6,10 +6,12 @@ import { InstantSSRAutoRefresh } from "@/components/SSRAutoRefresh";
|
||||
import { SlackBotTable } from "./SlackBotTable";
|
||||
import { useSlackBots } from "./[bot-id]/hooks";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import CreateButton from "@/refresh-components/buttons/CreateButton";
|
||||
import { DOCS_ADMINS_PATH } from "@/lib/constants";
|
||||
|
||||
const route = ADMIN_ROUTES.SLACK_BOTS;
|
||||
|
||||
function Main() {
|
||||
const {
|
||||
data: slackBots,
|
||||
@@ -75,8 +77,6 @@ function Main() {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.SLACK_BOTS]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header icon={route.icon} title={route.title} separator />
|
||||
|
||||
@@ -10,9 +10,9 @@ import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SvgLock } from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_PROCESSING]!;
|
||||
const route = ADMIN_ROUTES.DOCUMENT_PROCESSING;
|
||||
|
||||
function Main() {
|
||||
const {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import ImageGenerationContent from "./ImageGenerationContent";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.IMAGE_GENERATION]!;
|
||||
const route = ADMIN_ROUTES.IMAGE_GENERATION;
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
|
||||
@@ -19,9 +19,9 @@ import { SettingsContext } from "@/providers/SettingsProvider";
|
||||
import CardSection from "@/components/admin/CardSection";
|
||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||
import { useToastFromQuery } from "@/hooks/useToast";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.SEARCH_SETTINGS]!;
|
||||
const route = ADMIN_ROUTES.INDEX_SETTINGS;
|
||||
|
||||
export interface EmbeddingDetails {
|
||||
api_key: string;
|
||||
@@ -131,7 +131,7 @@ function Main() {
|
||||
|
||||
<div className="mt-4">
|
||||
<Button variant="action" href="/admin/embeddings">
|
||||
Update Search Settings
|
||||
Update Index Settings
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -464,7 +464,7 @@ export default function VoiceConfigurationPage() {
|
||||
return (
|
||||
<>
|
||||
<AdminPageTitle
|
||||
title="Voice Mode"
|
||||
title="Voice"
|
||||
icon={SvgMicrophone}
|
||||
includeDivider={false}
|
||||
/>
|
||||
@@ -484,7 +484,7 @@ export default function VoiceConfigurationPage() {
|
||||
return (
|
||||
<>
|
||||
<AdminPageTitle
|
||||
title="Voice Mode"
|
||||
title="Voice"
|
||||
icon={SvgMicrophone}
|
||||
includeDivider={false}
|
||||
/>
|
||||
@@ -497,7 +497,7 @@ export default function VoiceConfigurationPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminPageTitle icon={SvgAudio} title="Voice Mode" />
|
||||
<AdminPageTitle icon={SvgAudio} title="Voice" />
|
||||
<div className="pt-4 pb-4">
|
||||
<Text as="p" secondaryBody text03>
|
||||
Speech to text (STT) and text to speech (TTS) capabilities.
|
||||
|
||||
@@ -23,10 +23,10 @@ import {
|
||||
SvgOnyxLogo,
|
||||
SvgX,
|
||||
} from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { WebProviderSetupModal } from "@/app/admin/configuration/web-search/WebProviderSetupModal";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.WEB_SEARCH]!;
|
||||
const route = ADMIN_ROUTES.WEB_SEARCH;
|
||||
import {
|
||||
SEARCH_PROVIDERS_URL,
|
||||
SEARCH_PROVIDER_DETAILS,
|
||||
|
||||
@@ -16,9 +16,9 @@ import { Card } from "@/components/ui/card";
|
||||
import Text from "@/components/ui/text";
|
||||
import { Spinner } from "@/components/Spinner";
|
||||
import { SvgDownloadCloud } from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DEBUG]!;
|
||||
const route = ADMIN_ROUTES.DEBUG;
|
||||
|
||||
function Main() {
|
||||
const [categories, setCategories] = useState<string[]>([]);
|
||||
|
||||
@@ -19,7 +19,9 @@ import {
|
||||
import { createGuildConfig } from "@/app/admin/discord-bot/lib";
|
||||
import { DiscordGuildsTable } from "@/app/admin/discord-bot/DiscordGuildsTable";
|
||||
import { BotConfigCard } from "@/app/admin/discord-bot/BotConfigCard";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTES.DISCORD_BOTS;
|
||||
|
||||
function DiscordBotContent() {
|
||||
const { data: guilds, isLoading, error, refreshGuilds } = useDiscordGuilds();
|
||||
@@ -118,8 +120,6 @@ function DiscordBotContent() {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DISCORD_BOTS]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import { useState } from "react";
|
||||
import useSWR from "swr";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.INDEX_MIGRATION]!;
|
||||
const route = ADMIN_ROUTES.INDEX_MIGRATION;
|
||||
|
||||
import Card from "@/refresh-components/cards/Card";
|
||||
import { Content, ContentAction } from "@opal/layouts";
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { Explorer } from "./Explorer";
|
||||
import { Connector } from "@/lib/connectors/connectors";
|
||||
import { DocumentSetSummary } from "@/lib/types";
|
||||
|
||||
const route = ADMIN_ROUTES.DOCUMENT_EXPLORER;
|
||||
|
||||
interface DocumentExplorerPageProps {
|
||||
initialSearchValue: string | undefined;
|
||||
connectors: Connector<any>[];
|
||||
@@ -17,8 +19,6 @@ export default function DocumentExplorerPage({
|
||||
connectors,
|
||||
documentSets,
|
||||
}: DocumentExplorerPageProps) {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_EXPLORER]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header icon={route.icon} title={route.title} separator />
|
||||
|
||||
@@ -6,7 +6,9 @@ import { DocumentFeedbackTable } from "./DocumentFeedbackTable";
|
||||
import { numPages, numToDisplay } from "./constants";
|
||||
import Title from "@/components/ui/title";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTES.DOCUMENT_FEEDBACK;
|
||||
|
||||
function Main() {
|
||||
const {
|
||||
@@ -61,8 +63,6 @@ function Main() {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_FEEDBACK]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header icon={route.icon} title={route.title} separator />
|
||||
|
||||
@@ -6,12 +6,14 @@ import { refreshDocumentSets, useDocumentSets } from "../hooks";
|
||||
import { useConnectorStatus, useUserGroups } from "@/lib/hooks";
|
||||
import { ThreeDotsLoader } from "@/components/Loading";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import CardSection from "@/components/admin/CardSection";
|
||||
import { DocumentSetCreationForm } from "../DocumentSetCreationForm";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useVectorDbEnabled } from "@/providers/SettingsProvider";
|
||||
|
||||
const route = ADMIN_ROUTES.DOCUMENT_SETS;
|
||||
|
||||
function Main({ documentSetId }: { documentSetId: number }) {
|
||||
const router = useRouter();
|
||||
const vectorDbEnabled = useVectorDbEnabled();
|
||||
@@ -93,7 +95,6 @@ export default function Page(props: {
|
||||
}) {
|
||||
const params = use(props.params);
|
||||
const documentSetId = parseInt(params.documentSetId);
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_SETS]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { DocumentSetCreationForm } from "../DocumentSetCreationForm";
|
||||
import { useConnectorStatus, useUserGroups } from "@/lib/hooks";
|
||||
import { ThreeDotsLoader } from "@/components/Loading";
|
||||
@@ -11,6 +11,8 @@ import { refreshDocumentSets } from "../hooks";
|
||||
import CardSection from "@/components/admin/CardSection";
|
||||
import { useVectorDbEnabled } from "@/providers/SettingsProvider";
|
||||
|
||||
const route = ADMIN_ROUTES.DOCUMENT_SETS;
|
||||
|
||||
function Main() {
|
||||
const router = useRouter();
|
||||
const vectorDbEnabled = useVectorDbEnabled();
|
||||
@@ -58,8 +60,6 @@ function Main() {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_SETS]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ConnectorTitle } from "@/components/admin/connectors/ConnectorTitle";
|
||||
import { deleteDocumentSet } from "./lib";
|
||||
import { toast } from "@/hooks/useToast";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import {
|
||||
FiAlertTriangle,
|
||||
FiCheckCircle,
|
||||
@@ -43,6 +43,7 @@ import CreateButton from "@/refresh-components/buttons/CreateButton";
|
||||
import { SourceIcon } from "@/components/SourceIcon";
|
||||
import Link from "next/link";
|
||||
|
||||
const route = ADMIN_ROUTES.DOCUMENT_SETS;
|
||||
const numToDisplay = 50;
|
||||
|
||||
// Component to display federated connectors with consistent styling
|
||||
@@ -422,8 +423,6 @@ function Main() {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.DOCUMENT_SETS]!;
|
||||
|
||||
return (
|
||||
<SettingsLayouts.Root>
|
||||
<SettingsLayouts.Header icon={route.icon} title={route.title} separator />
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CCPairIndexingStatusTable } from "./CCPairIndexingStatusTable";
|
||||
import { SearchAndFilterControls } from "./SearchAndFilterControls";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import Link from "next/link";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import Text from "@/components/ui/text";
|
||||
import { useConnectorIndexingStatusWithPagination } from "@/lib/hooks";
|
||||
import { useToastFromQuery } from "@/hooks/useToast";
|
||||
@@ -18,6 +18,8 @@ import { TOGGLED_CONNECTORS_COOKIE_NAME } from "@/lib/constants";
|
||||
import { ConnectorStaggeredSkeleton } from "./ConnectorRowSkeleton";
|
||||
import { IndexingStatusRequest } from "@/lib/types";
|
||||
|
||||
const route = ADMIN_ROUTES.INDEXING_STATUS;
|
||||
|
||||
function Main() {
|
||||
const vectorDbEnabled = useVectorDbEnabled();
|
||||
|
||||
@@ -204,8 +206,6 @@ function Main() {
|
||||
}
|
||||
|
||||
export default function Status() {
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.INDEXING_STATUS]!;
|
||||
|
||||
useToastFromQuery({
|
||||
"connector-created": {
|
||||
message: "Connector created successfully",
|
||||
|
||||
@@ -31,9 +31,9 @@ import KGEntityTypes from "@/app/admin/kg/KGEntityTypes";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SvgSettings } from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.KNOWLEDGE_GRAPH]!;
|
||||
const route = ADMIN_ROUTES.KNOWLEDGE_GRAPH;
|
||||
|
||||
function createDomainField(
|
||||
name: string,
|
||||
|
||||
@@ -18,9 +18,9 @@ import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidE
|
||||
import CreateButton from "@/refresh-components/buttons/CreateButton";
|
||||
import { SvgGlobe, SvgUser, SvgUsers } from "@opal/icons";
|
||||
import { Section } from "@/layouts/general-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.TOKEN_RATE_LIMITS]!;
|
||||
const route = ADMIN_ROUTES.TOKEN_RATE_LIMITS;
|
||||
const BASE_URL = "/api/admin/token-rate-limits";
|
||||
const GLOBAL_TOKEN_FETCH_URL = `${BASE_URL}/global`;
|
||||
const USER_TOKEN_FETCH_URL = `${BASE_URL}/users`;
|
||||
|
||||
@@ -79,7 +79,7 @@ interface SelectedConnectorState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Admin Settings - Connector configuration page
|
||||
* Build Admin Panel - Connector configuration page
|
||||
*
|
||||
* Renders in the center panel area (replacing ChatPanel + OutputPanel).
|
||||
* Uses SettingsLayouts like AgentEditorPage does.
|
||||
|
||||
@@ -6,11 +6,11 @@ import { useSpecificUserGroup } from "./hook";
|
||||
import { ThreeDotsLoader } from "@/components/Loading";
|
||||
import { useConnectorStatus } from "@/lib/hooks";
|
||||
import useUsers from "@/hooks/useUsers";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { useVectorDbEnabled } from "@/providers/SettingsProvider";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.GROUPS]!;
|
||||
const route = ADMIN_ROUTES.GROUPS;
|
||||
|
||||
function Main({ groupId }: { groupId: string }) {
|
||||
const vectorDbEnabled = useVectorDbEnabled();
|
||||
|
||||
@@ -8,11 +8,11 @@ import { useConnectorStatus, useUserGroups } from "@/lib/hooks";
|
||||
import useUsers from "@/hooks/useUsers";
|
||||
import { useUser } from "@/providers/UserProvider";
|
||||
import CreateButton from "@/refresh-components/buttons/CreateButton";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { useVectorDbEnabled } from "@/providers/SettingsProvider";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.GROUPS]!;
|
||||
const route = ADMIN_ROUTES.GROUPS;
|
||||
|
||||
function Main() {
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { CUSTOM_ANALYTICS_ENABLED } from "@/lib/constants";
|
||||
import { Callout } from "@/components/ui/callout";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import Text from "@/components/ui/text";
|
||||
import { CustomAnalyticsUpdateForm } from "./CustomAnalyticsUpdateForm";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.CUSTOM_ANALYTICS]!;
|
||||
const route = ADMIN_ROUTES.CUSTOM_ANALYTICS;
|
||||
|
||||
function Main() {
|
||||
if (!CUSTOM_ANALYTICS_ENABLED) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { QueryHistoryTable } from "@/app/ee/admin/performance/query-history/QueryHistoryTable";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.QUERY_HISTORY]!;
|
||||
const route = ADMIN_ROUTES.QUERY_HISTORY;
|
||||
|
||||
export default function QueryHistoryPage() {
|
||||
return (
|
||||
|
||||
@@ -9,10 +9,10 @@ import { useTimeRange } from "@/app/ee/admin/performance/lib";
|
||||
import UsageReports from "@/app/ee/admin/performance/usage/UsageReports";
|
||||
import Separator from "@/refresh-components/Separator";
|
||||
import { useAdminPersonas } from "@/hooks/useAdminPersonas";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.USAGE]!;
|
||||
const route = ADMIN_ROUTES.USAGE;
|
||||
|
||||
export default function AnalyticsPage() {
|
||||
const [timeRange, setTimeRange] = useTimeRange();
|
||||
|
||||
@@ -2,10 +2,10 @@ import { StandardAnswerCreationForm } from "@/app/ee/admin/standard-answer/Stand
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { StandardAnswer, StandardAnswerCategory } from "@/lib/types";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.STANDARD_ANSWERS]!;
|
||||
const route = ADMIN_ROUTES.STANDARD_ANSWERS;
|
||||
|
||||
async function Main({ id }: { id: string }) {
|
||||
const tasks = [
|
||||
|
||||
@@ -2,10 +2,10 @@ import { StandardAnswerCreationForm } from "@/app/ee/admin/standard-answer/Stand
|
||||
import { fetchSS } from "@/lib/utilsSS";
|
||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { StandardAnswerCategory } from "@/lib/types";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.STANDARD_ANSWERS]!;
|
||||
const route = ADMIN_ROUTES.STANDARD_ANSWERS;
|
||||
|
||||
async function Page() {
|
||||
const standardAnswerCategoriesResponse = await fetchSS(
|
||||
|
||||
@@ -30,10 +30,10 @@ import { TableHeader } from "@/components/ui/table";
|
||||
import CreateButton from "@/refresh-components/buttons/CreateButton";
|
||||
import { SvgEdit, SvgTrash } from "@opal/icons";
|
||||
import { Button } from "@opal/components";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
const NUM_RESULTS_PER_PAGE = 10;
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.STANDARD_ANSWERS]!;
|
||||
const route = ADMIN_ROUTES.STANDARD_ANSWERS;
|
||||
|
||||
type Displayable = JSX.Element | string;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { Button } from "@opal/components";
|
||||
import { Disabled } from "@opal/core";
|
||||
import {
|
||||
@@ -16,7 +16,7 @@ import * as Yup from "yup";
|
||||
import { EnterpriseSettings } from "@/interfaces/settings";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.THEME]!;
|
||||
const route = ADMIN_ROUTES.THEME;
|
||||
|
||||
const CHAR_LIMITS = {
|
||||
application_name: 50,
|
||||
|
||||
@@ -6,11 +6,10 @@ import { useSettingsContext } from "@/providers/SettingsProvider";
|
||||
import { ApplicationStatus } from "@/interfaces/settings";
|
||||
import { Button } from "@opal/components";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
export interface ClientLayoutProps {
|
||||
children: React.ReactNode;
|
||||
enableEnterprise: boolean;
|
||||
enableCloud: boolean;
|
||||
}
|
||||
|
||||
@@ -19,40 +18,36 @@ export interface ClientLayoutProps {
|
||||
// the `py-10 px-4 md:px-12` padding below can be removed entirely and
|
||||
// this prefix list can be deleted.
|
||||
const SETTINGS_LAYOUT_PREFIXES = [
|
||||
ADMIN_PATHS.CHAT_PREFERENCES,
|
||||
ADMIN_PATHS.IMAGE_GENERATION,
|
||||
ADMIN_PATHS.WEB_SEARCH,
|
||||
ADMIN_PATHS.MCP_ACTIONS,
|
||||
ADMIN_PATHS.OPENAPI_ACTIONS,
|
||||
ADMIN_PATHS.BILLING,
|
||||
ADMIN_PATHS.INDEX_MIGRATION,
|
||||
ADMIN_PATHS.DISCORD_BOTS,
|
||||
ADMIN_PATHS.THEME,
|
||||
ADMIN_PATHS.LLM_MODELS,
|
||||
ADMIN_PATHS.AGENTS,
|
||||
ADMIN_PATHS.USERS,
|
||||
ADMIN_PATHS.TOKEN_RATE_LIMITS,
|
||||
ADMIN_PATHS.SEARCH_SETTINGS,
|
||||
ADMIN_PATHS.DOCUMENT_PROCESSING,
|
||||
ADMIN_PATHS.CODE_INTERPRETER,
|
||||
ADMIN_PATHS.API_KEYS,
|
||||
ADMIN_PATHS.ADD_CONNECTOR,
|
||||
ADMIN_PATHS.INDEXING_STATUS,
|
||||
ADMIN_PATHS.DOCUMENTS,
|
||||
ADMIN_PATHS.DEBUG,
|
||||
ADMIN_PATHS.KNOWLEDGE_GRAPH,
|
||||
ADMIN_PATHS.SLACK_BOTS,
|
||||
ADMIN_PATHS.STANDARD_ANSWERS,
|
||||
ADMIN_PATHS.GROUPS,
|
||||
ADMIN_PATHS.PERFORMANCE,
|
||||
ADMIN_PATHS.SCIM,
|
||||
ADMIN_ROUTES.CHAT_PREFERENCES.path,
|
||||
ADMIN_ROUTES.IMAGE_GENERATION.path,
|
||||
ADMIN_ROUTES.WEB_SEARCH.path,
|
||||
ADMIN_ROUTES.MCP_ACTIONS.path,
|
||||
ADMIN_ROUTES.OPENAPI_ACTIONS.path,
|
||||
ADMIN_ROUTES.BILLING.path,
|
||||
ADMIN_ROUTES.INDEX_MIGRATION.path,
|
||||
ADMIN_ROUTES.DISCORD_BOTS.path,
|
||||
ADMIN_ROUTES.THEME.path,
|
||||
ADMIN_ROUTES.LLM_MODELS.path,
|
||||
ADMIN_ROUTES.AGENTS.path,
|
||||
ADMIN_ROUTES.USERS.path,
|
||||
ADMIN_ROUTES.TOKEN_RATE_LIMITS.path,
|
||||
ADMIN_ROUTES.INDEX_SETTINGS.path,
|
||||
ADMIN_ROUTES.DOCUMENT_PROCESSING.path,
|
||||
ADMIN_ROUTES.CODE_INTERPRETER.path,
|
||||
ADMIN_ROUTES.API_KEYS.path,
|
||||
ADMIN_ROUTES.ADD_CONNECTOR.path,
|
||||
ADMIN_ROUTES.INDEXING_STATUS.path,
|
||||
ADMIN_ROUTES.DOCUMENTS.path,
|
||||
ADMIN_ROUTES.DEBUG.path,
|
||||
ADMIN_ROUTES.KNOWLEDGE_GRAPH.path,
|
||||
ADMIN_ROUTES.SLACK_BOTS.path,
|
||||
ADMIN_ROUTES.STANDARD_ANSWERS.path,
|
||||
ADMIN_ROUTES.GROUPS.path,
|
||||
ADMIN_ROUTES.PERFORMANCE.path,
|
||||
ADMIN_ROUTES.SCIM.path,
|
||||
];
|
||||
|
||||
export function ClientLayout({
|
||||
children,
|
||||
enableEnterprise,
|
||||
enableCloud,
|
||||
}: ClientLayoutProps) {
|
||||
export function ClientLayout({ children, enableCloud }: ClientLayoutProps) {
|
||||
const pathname = usePathname();
|
||||
const settings = useSettingsContext();
|
||||
|
||||
@@ -86,10 +81,7 @@ export function ClientLayout({
|
||||
<div className="flex-1 min-w-0 min-h-0 overflow-y-auto">{children}</div>
|
||||
) : (
|
||||
<>
|
||||
<AdminSidebar
|
||||
enableCloudSS={enableCloud}
|
||||
enableEnterpriseSS={enableEnterprise}
|
||||
/>
|
||||
<AdminSidebar enableCloudSS={enableCloud} />
|
||||
<div
|
||||
data-main-container
|
||||
className={cn(
|
||||
|
||||
@@ -2,10 +2,7 @@ import { redirect } from "next/navigation";
|
||||
import type { Route } from "next";
|
||||
import { requireAdminAuth } from "@/lib/auth/requireAuth";
|
||||
import { ClientLayout } from "./ClientLayout";
|
||||
import {
|
||||
NEXT_PUBLIC_CLOUD_ENABLED,
|
||||
SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED,
|
||||
} from "@/lib/constants";
|
||||
import { NEXT_PUBLIC_CLOUD_ENABLED } from "@/lib/constants";
|
||||
import { AnnouncementBanner } from "../header/AnnouncementBanner";
|
||||
|
||||
export interface LayoutProps {
|
||||
@@ -22,10 +19,7 @@ export default async function Layout({ children }: LayoutProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ClientLayout
|
||||
enableEnterprise={SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED}
|
||||
enableCloud={NEXT_PUBLIC_CLOUD_ENABLED}
|
||||
>
|
||||
<ClientLayout enableCloud={NEXT_PUBLIC_CLOUD_ENABLED}>
|
||||
<AnnouncementBanner />
|
||||
{children}
|
||||
</ClientLayout>
|
||||
|
||||
@@ -22,10 +22,10 @@ export default function NoAgentModal() {
|
||||
<>
|
||||
<Text as="p">
|
||||
As an administrator, you can create a new agent by visiting the
|
||||
admin settings.
|
||||
admin panel.
|
||||
</Text>
|
||||
<Button width="full" href="/admin/agents">
|
||||
Go to Admin Settings
|
||||
Go to Admin Panel
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -65,6 +65,7 @@ import useAppFocus from "@/hooks/useAppFocus";
|
||||
import { useQueryController } from "@/providers/QueryControllerProvider";
|
||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||
import useBrowserInfo from "@/hooks/useBrowserInfo";
|
||||
import { APP_SLOGAN } from "@/lib/constants";
|
||||
|
||||
/**
|
||||
* App Header Component
|
||||
@@ -461,7 +462,7 @@ function Footer() {
|
||||
settings?.enterpriseSettings?.custom_lower_disclaimer_content ||
|
||||
`[Onyx ${
|
||||
settings?.webVersion || "dev"
|
||||
}](https://www.onyx.app/) - Open Source AI Platform`;
|
||||
}](https://www.onyx.app/) - ${APP_SLOGAN}`;
|
||||
|
||||
return (
|
||||
<footer
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
SvgActions,
|
||||
SvgActivity,
|
||||
SvgArrowExchange,
|
||||
SvgAudio,
|
||||
SvgBarChart,
|
||||
SvgBookOpen,
|
||||
SvgBubbleText,
|
||||
@@ -10,243 +11,254 @@ import {
|
||||
SvgCpu,
|
||||
SvgDiscordMono,
|
||||
SvgDownload,
|
||||
SvgEmpty,
|
||||
SvgFileText,
|
||||
SvgFolder,
|
||||
SvgFiles,
|
||||
SvgGlobe,
|
||||
SvgHistory,
|
||||
SvgImage,
|
||||
SvgKey,
|
||||
SvgMcp,
|
||||
SvgNetworkGraph,
|
||||
SvgOnyxOctagon,
|
||||
SvgPaintBrush,
|
||||
SvgSearch,
|
||||
SvgServer,
|
||||
SvgShield,
|
||||
SvgProgressBars,
|
||||
SvgSearchMenu,
|
||||
SvgSlack,
|
||||
SvgTerminal,
|
||||
SvgThumbsUp,
|
||||
SvgUploadCloud,
|
||||
SvgUser,
|
||||
SvgUserKey,
|
||||
SvgUserSync,
|
||||
SvgUsers,
|
||||
SvgWallet,
|
||||
SvgZoomIn,
|
||||
} from "@opal/icons";
|
||||
|
||||
/**
|
||||
* Canonical path constants for every admin route.
|
||||
*/
|
||||
export const ADMIN_PATHS = {
|
||||
INDEXING_STATUS: "/admin/indexing/status",
|
||||
ADD_CONNECTOR: "/admin/add-connector",
|
||||
DOCUMENT_SETS: "/admin/documents/sets",
|
||||
DOCUMENT_EXPLORER: "/admin/documents/explorer",
|
||||
DOCUMENT_FEEDBACK: "/admin/documents/feedback",
|
||||
AGENTS: "/admin/agents",
|
||||
SLACK_BOTS: "/admin/bots",
|
||||
DISCORD_BOTS: "/admin/discord-bot",
|
||||
MCP_ACTIONS: "/admin/actions/mcp",
|
||||
OPENAPI_ACTIONS: "/admin/actions/open-api",
|
||||
STANDARD_ANSWERS: "/admin/standard-answer",
|
||||
GROUPS: "/admin/groups",
|
||||
CHAT_PREFERENCES: "/admin/configuration/chat-preferences",
|
||||
LLM_MODELS: "/admin/configuration/llm",
|
||||
WEB_SEARCH: "/admin/configuration/web-search",
|
||||
IMAGE_GENERATION: "/admin/configuration/image-generation",
|
||||
CODE_INTERPRETER: "/admin/configuration/code-interpreter",
|
||||
SEARCH_SETTINGS: "/admin/configuration/search",
|
||||
DOCUMENT_PROCESSING: "/admin/configuration/document-processing",
|
||||
KNOWLEDGE_GRAPH: "/admin/kg",
|
||||
USERS: "/admin/users",
|
||||
API_KEYS: "/admin/api-key",
|
||||
TOKEN_RATE_LIMITS: "/admin/token-rate-limits",
|
||||
USAGE: "/admin/performance/usage",
|
||||
QUERY_HISTORY: "/admin/performance/query-history",
|
||||
CUSTOM_ANALYTICS: "/admin/performance/custom-analytics",
|
||||
THEME: "/admin/theme",
|
||||
BILLING: "/admin/billing",
|
||||
INDEX_MIGRATION: "/admin/document-index-migration",
|
||||
SCIM: "/admin/scim",
|
||||
DEBUG: "/admin/debug",
|
||||
// Prefix-only entries (used in SETTINGS_LAYOUT_PREFIXES but have no
|
||||
// single page header of their own)
|
||||
DOCUMENTS: "/admin/documents",
|
||||
PERFORMANCE: "/admin/performance",
|
||||
} as const;
|
||||
|
||||
interface AdminRouteConfig {
|
||||
export interface AdminRouteEntry {
|
||||
path: string;
|
||||
icon: IconFunctionComponent;
|
||||
title: string;
|
||||
sidebarLabel: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single source of truth for icon, page-header title, and sidebar label
|
||||
* for every admin route. Keyed by path from `ADMIN_PATHS`.
|
||||
* Single source of truth for every admin route: path, icon, page-header
|
||||
* title, and sidebar label.
|
||||
*/
|
||||
export const ADMIN_ROUTE_CONFIG: Record<string, AdminRouteConfig> = {
|
||||
[ADMIN_PATHS.INDEXING_STATUS]: {
|
||||
export const ADMIN_ROUTES = {
|
||||
INDEXING_STATUS: {
|
||||
path: "/admin/indexing/status",
|
||||
icon: SvgBookOpen,
|
||||
title: "Existing Connectors",
|
||||
sidebarLabel: "Existing Connectors",
|
||||
},
|
||||
[ADMIN_PATHS.ADD_CONNECTOR]: {
|
||||
ADD_CONNECTOR: {
|
||||
path: "/admin/add-connector",
|
||||
icon: SvgUploadCloud,
|
||||
title: "Add Connector",
|
||||
sidebarLabel: "Add Connector",
|
||||
},
|
||||
[ADMIN_PATHS.DOCUMENT_SETS]: {
|
||||
icon: SvgFolder,
|
||||
DOCUMENT_SETS: {
|
||||
path: "/admin/documents/sets",
|
||||
icon: SvgFiles,
|
||||
title: "Document Sets",
|
||||
sidebarLabel: "Document Sets",
|
||||
},
|
||||
[ADMIN_PATHS.DOCUMENT_EXPLORER]: {
|
||||
DOCUMENT_EXPLORER: {
|
||||
path: "/admin/documents/explorer",
|
||||
icon: SvgZoomIn,
|
||||
title: "Document Explorer",
|
||||
sidebarLabel: "Explorer",
|
||||
},
|
||||
[ADMIN_PATHS.DOCUMENT_FEEDBACK]: {
|
||||
DOCUMENT_FEEDBACK: {
|
||||
path: "/admin/documents/feedback",
|
||||
icon: SvgThumbsUp,
|
||||
title: "Document Feedback",
|
||||
sidebarLabel: "Feedback",
|
||||
},
|
||||
[ADMIN_PATHS.AGENTS]: {
|
||||
AGENTS: {
|
||||
path: "/admin/agents",
|
||||
icon: SvgOnyxOctagon,
|
||||
title: "Agents",
|
||||
sidebarLabel: "Agents",
|
||||
},
|
||||
[ADMIN_PATHS.SLACK_BOTS]: {
|
||||
SLACK_BOTS: {
|
||||
path: "/admin/bots",
|
||||
icon: SvgSlack,
|
||||
title: "Slack Bots",
|
||||
sidebarLabel: "Slack Bots",
|
||||
title: "Slack Integration",
|
||||
sidebarLabel: "Slack Integration",
|
||||
},
|
||||
[ADMIN_PATHS.DISCORD_BOTS]: {
|
||||
DISCORD_BOTS: {
|
||||
path: "/admin/discord-bot",
|
||||
icon: SvgDiscordMono,
|
||||
title: "Discord Bots",
|
||||
sidebarLabel: "Discord Bots",
|
||||
title: "Discord Integration",
|
||||
sidebarLabel: "Discord Integration",
|
||||
},
|
||||
[ADMIN_PATHS.MCP_ACTIONS]: {
|
||||
MCP_ACTIONS: {
|
||||
path: "/admin/actions/mcp",
|
||||
icon: SvgMcp,
|
||||
title: "MCP Actions",
|
||||
sidebarLabel: "MCP Actions",
|
||||
},
|
||||
[ADMIN_PATHS.OPENAPI_ACTIONS]: {
|
||||
OPENAPI_ACTIONS: {
|
||||
path: "/admin/actions/open-api",
|
||||
icon: SvgActions,
|
||||
title: "OpenAPI Actions",
|
||||
sidebarLabel: "OpenAPI Actions",
|
||||
},
|
||||
[ADMIN_PATHS.STANDARD_ANSWERS]: {
|
||||
STANDARD_ANSWERS: {
|
||||
path: "/admin/standard-answer",
|
||||
icon: SvgClipboard,
|
||||
title: "Standard Answers",
|
||||
sidebarLabel: "Standard Answers",
|
||||
},
|
||||
[ADMIN_PATHS.GROUPS]: {
|
||||
GROUPS: {
|
||||
path: "/admin/groups",
|
||||
icon: SvgUsers,
|
||||
title: "Manage User Groups",
|
||||
sidebarLabel: "Groups",
|
||||
},
|
||||
[ADMIN_PATHS.CHAT_PREFERENCES]: {
|
||||
CHAT_PREFERENCES: {
|
||||
path: "/admin/configuration/chat-preferences",
|
||||
icon: SvgBubbleText,
|
||||
title: "Chat Preferences",
|
||||
sidebarLabel: "Chat Preferences",
|
||||
},
|
||||
[ADMIN_PATHS.LLM_MODELS]: {
|
||||
LLM_MODELS: {
|
||||
path: "/admin/configuration/llm",
|
||||
icon: SvgCpu,
|
||||
title: "Language Models",
|
||||
sidebarLabel: "Language Models",
|
||||
},
|
||||
[ADMIN_PATHS.WEB_SEARCH]: {
|
||||
WEB_SEARCH: {
|
||||
path: "/admin/configuration/web-search",
|
||||
icon: SvgGlobe,
|
||||
title: "Web Search",
|
||||
sidebarLabel: "Web Search",
|
||||
},
|
||||
[ADMIN_PATHS.IMAGE_GENERATION]: {
|
||||
IMAGE_GENERATION: {
|
||||
path: "/admin/configuration/image-generation",
|
||||
icon: SvgImage,
|
||||
title: "Image Generation",
|
||||
sidebarLabel: "Image Generation",
|
||||
},
|
||||
[ADMIN_PATHS.CODE_INTERPRETER]: {
|
||||
VOICE: {
|
||||
path: "/admin/configuration/voice",
|
||||
icon: SvgAudio,
|
||||
title: "Voice",
|
||||
sidebarLabel: "Voice",
|
||||
},
|
||||
CODE_INTERPRETER: {
|
||||
path: "/admin/configuration/code-interpreter",
|
||||
icon: SvgTerminal,
|
||||
title: "Code Interpreter",
|
||||
sidebarLabel: "Code Interpreter",
|
||||
},
|
||||
[ADMIN_PATHS.SEARCH_SETTINGS]: {
|
||||
icon: SvgSearch,
|
||||
title: "Search Settings",
|
||||
sidebarLabel: "Search Settings",
|
||||
INDEX_SETTINGS: {
|
||||
path: "/admin/configuration/search",
|
||||
icon: SvgSearchMenu,
|
||||
title: "Index Settings",
|
||||
sidebarLabel: "Index Settings",
|
||||
},
|
||||
[ADMIN_PATHS.DOCUMENT_PROCESSING]: {
|
||||
DOCUMENT_PROCESSING: {
|
||||
path: "/admin/configuration/document-processing",
|
||||
icon: SvgFileText,
|
||||
title: "Document Processing",
|
||||
sidebarLabel: "Document Processing",
|
||||
},
|
||||
[ADMIN_PATHS.KNOWLEDGE_GRAPH]: {
|
||||
KNOWLEDGE_GRAPH: {
|
||||
path: "/admin/kg",
|
||||
icon: SvgNetworkGraph,
|
||||
title: "Knowledge Graph",
|
||||
sidebarLabel: "Knowledge Graph",
|
||||
},
|
||||
[ADMIN_PATHS.USERS]: {
|
||||
USERS: {
|
||||
path: "/admin/users",
|
||||
icon: SvgUser,
|
||||
title: "Users & Requests",
|
||||
sidebarLabel: "Users",
|
||||
},
|
||||
[ADMIN_PATHS.API_KEYS]: {
|
||||
icon: SvgKey,
|
||||
title: "API Keys",
|
||||
sidebarLabel: "API Keys",
|
||||
API_KEYS: {
|
||||
path: "/admin/api-key",
|
||||
icon: SvgUserKey,
|
||||
title: "Service Accounts",
|
||||
sidebarLabel: "Service Accounts",
|
||||
},
|
||||
[ADMIN_PATHS.TOKEN_RATE_LIMITS]: {
|
||||
icon: SvgShield,
|
||||
title: "Token Rate Limits",
|
||||
sidebarLabel: "Token Rate Limits",
|
||||
TOKEN_RATE_LIMITS: {
|
||||
path: "/admin/token-rate-limits",
|
||||
icon: SvgProgressBars,
|
||||
title: "Spending Limits",
|
||||
sidebarLabel: "Spending Limits",
|
||||
},
|
||||
[ADMIN_PATHS.USAGE]: {
|
||||
USAGE: {
|
||||
path: "/admin/performance/usage",
|
||||
icon: SvgActivity,
|
||||
title: "Usage Statistics",
|
||||
sidebarLabel: "Usage Statistics",
|
||||
},
|
||||
[ADMIN_PATHS.QUERY_HISTORY]: {
|
||||
icon: SvgServer,
|
||||
QUERY_HISTORY: {
|
||||
path: "/admin/performance/query-history",
|
||||
icon: SvgHistory,
|
||||
title: "Query History",
|
||||
sidebarLabel: "Query History",
|
||||
},
|
||||
[ADMIN_PATHS.CUSTOM_ANALYTICS]: {
|
||||
CUSTOM_ANALYTICS: {
|
||||
path: "/admin/performance/custom-analytics",
|
||||
icon: SvgBarChart,
|
||||
title: "Custom Analytics",
|
||||
sidebarLabel: "Custom Analytics",
|
||||
},
|
||||
[ADMIN_PATHS.THEME]: {
|
||||
THEME: {
|
||||
path: "/admin/theme",
|
||||
icon: SvgPaintBrush,
|
||||
title: "Appearance & Theming",
|
||||
sidebarLabel: "Appearance & Theming",
|
||||
},
|
||||
[ADMIN_PATHS.BILLING]: {
|
||||
BILLING: {
|
||||
path: "/admin/billing",
|
||||
icon: SvgWallet,
|
||||
title: "Plans & Billing",
|
||||
sidebarLabel: "Plans & Billing",
|
||||
},
|
||||
[ADMIN_PATHS.INDEX_MIGRATION]: {
|
||||
INDEX_MIGRATION: {
|
||||
path: "/admin/document-index-migration",
|
||||
icon: SvgArrowExchange,
|
||||
title: "Document Index Migration",
|
||||
sidebarLabel: "Document Index Migration",
|
||||
},
|
||||
[ADMIN_PATHS.SCIM]: {
|
||||
SCIM: {
|
||||
path: "/admin/scim",
|
||||
icon: SvgUserSync,
|
||||
title: "SCIM",
|
||||
sidebarLabel: "SCIM",
|
||||
},
|
||||
[ADMIN_PATHS.DEBUG]: {
|
||||
DEBUG: {
|
||||
path: "/admin/debug",
|
||||
icon: SvgDownload,
|
||||
title: "Debug Logs",
|
||||
sidebarLabel: "Debug Logs",
|
||||
},
|
||||
};
|
||||
// Prefix-only entries used for layout matching — not rendered as sidebar
|
||||
// items or page headers.
|
||||
DOCUMENTS: {
|
||||
path: "/admin/documents",
|
||||
icon: SvgEmpty,
|
||||
title: "",
|
||||
sidebarLabel: "",
|
||||
},
|
||||
PERFORMANCE: {
|
||||
path: "/admin/performance",
|
||||
icon: SvgEmpty,
|
||||
title: "",
|
||||
sidebarLabel: "",
|
||||
},
|
||||
} as const satisfies Record<string, AdminRouteEntry>;
|
||||
|
||||
/**
|
||||
* Helper that converts a route config entry into the `{ name, icon, link }`
|
||||
* shape expected by the sidebar. Extra fields (e.g. `error`) can be spread in.
|
||||
* Helper that converts a route entry into the `{ name, icon, link }`
|
||||
* shape expected by the sidebar.
|
||||
*/
|
||||
export function sidebarItem(path: string) {
|
||||
const config = ADMIN_ROUTE_CONFIG[path]!;
|
||||
return { name: config.sidebarLabel, icon: config.icon, link: path };
|
||||
export function sidebarItem(route: AdminRouteEntry) {
|
||||
return { name: route.sidebarLabel, icon: route.icon, link: route.path };
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@ export const CREDENTIAL_JSON = "credential_json";
|
||||
|
||||
export const MODAL_ROOT_ID = "modal-root";
|
||||
|
||||
export const ANONYMOUS_USER_NAME = "Anonymous";
|
||||
export const UNNAMED_CHAT = "New Chat";
|
||||
|
||||
export const DEFAULT_AGENT_ID = 0;
|
||||
@@ -131,3 +130,5 @@ export const LOGO_UNFOLDED_SIZE_PX = 88;
|
||||
|
||||
export const DEFAULT_CONTEXT_TOKENS = 120_000;
|
||||
export const MAX_CHUNKS_FED_TO_CHAT = 25;
|
||||
|
||||
export const APP_SLOGAN = "Open Source AI Platform";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { User } from "./types";
|
||||
import { User } from "@/lib/types";
|
||||
|
||||
export const checkUserIsNoAuthUser = (userId: string) => {
|
||||
return userId === "__no_auth_user__";
|
||||
@@ -113,3 +113,18 @@ export async function refreshToken(
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function getUserDisplayName(user: User | null): string {
|
||||
// Prioritize custom personal name if set
|
||||
if (!!user?.personalization?.name) return user.personalization.name;
|
||||
// Then, prioritize personal email
|
||||
if (!!user?.email) {
|
||||
const atIndex = user.email.indexOf("@");
|
||||
if (atIndex > 0) {
|
||||
return user.email.substring(0, atIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing works, then fall back to anonymous user name
|
||||
return "Anonymous";
|
||||
}
|
||||
|
||||
@@ -766,7 +766,6 @@ export default function AppPage({ firstMessage }: ChatPageProps) {
|
||||
{(appFocus.isNewSession() || appFocus.isAgent()) &&
|
||||
(state.phase === "idle" ||
|
||||
state.phase === "classifying") &&
|
||||
!isLoadingOnboarding &&
|
||||
(showOnboarding || !user?.personalization?.name) &&
|
||||
!onboardingDismissed && (
|
||||
<OnboardingFlow
|
||||
|
||||
@@ -977,7 +977,7 @@ function ChatPreferencesSettings() {
|
||||
|
||||
<Section gap={0.75}>
|
||||
<Content
|
||||
title="Voice Mode"
|
||||
title="Voice"
|
||||
sizePreset="main-content"
|
||||
variant="section"
|
||||
widthVariant="full"
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
SvgFold,
|
||||
SvgExternalLink,
|
||||
} from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { Content } from "@opal/layouts";
|
||||
import {
|
||||
useSettingsContext,
|
||||
@@ -58,7 +58,7 @@ import useFilter from "@/hooks/useFilter";
|
||||
import { MCPServer } from "@/lib/tools/interfaces";
|
||||
import type { IconProps } from "@opal/types";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.CHAT_PREFERENCES]!;
|
||||
const route = ADMIN_ROUTES.CHAT_PREFERENCES;
|
||||
|
||||
interface DefaultAgentConfiguration {
|
||||
tool_ids: number[];
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
SvgUnplug,
|
||||
SvgXOctagon,
|
||||
} from "@opal/icons";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import { Section } from "@/layouts/general-layouts";
|
||||
import { Button } from "@opal/components";
|
||||
import { Disabled } from "@opal/core";
|
||||
@@ -23,7 +23,7 @@ import { updateCodeInterpreter } from "@/lib/admin/code-interpreter/svc";
|
||||
import { ContentAction } from "@opal/layouts";
|
||||
import { toast } from "@/hooks/useToast";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.CODE_INTERPRETER]!;
|
||||
const route = ADMIN_ROUTES.CODE_INTERPRETER;
|
||||
|
||||
interface CodeInterpreterCardProps {
|
||||
variant?: CardProps["variant"];
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Button } from "@opal/components";
|
||||
import { Hoverable } from "@opal/core";
|
||||
import { SvgArrowExchange, SvgSettings, SvgTrash } from "@opal/icons";
|
||||
import * as SettingsLayouts from "@/layouts/settings-layouts";
|
||||
import { ADMIN_ROUTE_CONFIG, ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
import * as GeneralLayouts from "@/layouts/general-layouts";
|
||||
import {
|
||||
getProviderDisplayName,
|
||||
@@ -47,7 +47,7 @@ import { LMStudioForm } from "@/sections/modals/llmConfig/LMStudioForm";
|
||||
import { LiteLLMProxyModal } from "@/sections/modals/llmConfig/LiteLLMProxyModal";
|
||||
import { Section } from "@/layouts/general-layouts";
|
||||
|
||||
const route = ADMIN_ROUTE_CONFIG[ADMIN_PATHS.LLM_MODELS]!;
|
||||
const route = ADMIN_ROUTES.LLM_MODELS;
|
||||
|
||||
// ============================================================================
|
||||
// Provider form mapping (keyed by provider name from the API)
|
||||
|
||||
@@ -6,7 +6,7 @@ import Card from "@/refresh-components/cards/Card";
|
||||
import IconButton from "@/refresh-components/buttons/IconButton";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import Link from "next/link";
|
||||
import { ADMIN_PATHS } from "@/lib/admin-routes";
|
||||
import { ADMIN_ROUTES } from "@/lib/admin-routes";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stats cell — number + label + hover filter icon
|
||||
@@ -67,7 +67,7 @@ function ScimCard() {
|
||||
variant="section"
|
||||
paddingVariant="fit"
|
||||
rightChildren={
|
||||
<Link href={ADMIN_PATHS.SCIM}>
|
||||
<Link href={ADMIN_ROUTES.SCIM.path}>
|
||||
<Button prominence="tertiary" rightIcon={SvgArrowUpRight} size="sm">
|
||||
Manage
|
||||
</Button>
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function ChatDocumentDisplay({
|
||||
) : (
|
||||
<SourceIcon sourceType={document.source_type} iconSize={18} />
|
||||
)}
|
||||
<Truncated className="line-clamp-2" side="left" disable>
|
||||
<Truncated className="line-clamp-2" side="left">
|
||||
{title}
|
||||
</Truncated>
|
||||
</div>
|
||||
|
||||
@@ -3,15 +3,7 @@
|
||||
import { MinimalOnyxDocument, OnyxDocument } from "@/lib/search/interfaces";
|
||||
import ChatDocumentDisplay from "@/sections/document-sidebar/ChatDocumentDisplay";
|
||||
import { removeDuplicateDocs } from "@/lib/documentUtils";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useMemo,
|
||||
memo,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { Dispatch, SetStateAction, useMemo, memo } from "react";
|
||||
import { getCitations } from "@/app/app/services/packetUtils";
|
||||
import {
|
||||
useCurrentMessageTree,
|
||||
@@ -48,30 +40,25 @@ const buildOnyxDocumentFromFile = (
|
||||
|
||||
interface HeaderProps {
|
||||
children: string;
|
||||
onClose?: () => void;
|
||||
isTop?: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function Header({ children, onClose, isTop }: HeaderProps) {
|
||||
function Header({ children, onClose }: HeaderProps) {
|
||||
return (
|
||||
<div className="sticky top-0 z-sticky bg-background-tint-01">
|
||||
<div className="flex flex-row w-full items-center justify-between gap-2 py-3">
|
||||
<div className="flex items-center gap-2 w-full px-3">
|
||||
{isTop && (
|
||||
<SvgSearchMenu className="w-[1.3rem] h-[1.3rem] stroke-text-03" />
|
||||
)}
|
||||
<SvgSearchMenu className="w-[1.3rem] h-[1.3rem] stroke-text-03" />
|
||||
<Text as="p" headingH3 text03>
|
||||
{children}
|
||||
</Text>
|
||||
</div>
|
||||
{onClose && (
|
||||
<Button
|
||||
icon={SvgX}
|
||||
prominence="tertiary"
|
||||
onClick={onClose}
|
||||
tooltip="Close Sidebar"
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
icon={SvgX}
|
||||
prominence="tertiary"
|
||||
onClick={onClose}
|
||||
tooltip="Close Sidebar"
|
||||
/>
|
||||
</div>
|
||||
<Separator noPadding />
|
||||
</div>
|
||||
@@ -135,26 +122,6 @@ const DocumentsSidebar = memo(
|
||||
return { citedDocumentIds, citationOrder };
|
||||
}, [idOfMessageToDisplay, selectedMessage?.packets.length]);
|
||||
|
||||
const observerRef = useRef<IntersectionObserver | null>(null);
|
||||
const [isMoreStuck, setIsMoreStuck] = useState(false);
|
||||
|
||||
const moreSentinelRef = useCallback((node: HTMLDivElement | null) => {
|
||||
observerRef.current?.disconnect();
|
||||
observerRef.current = null;
|
||||
|
||||
if (!node) return;
|
||||
|
||||
const root = node.closest("#onyx-chat-sidebar");
|
||||
if (!root) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => setIsMoreStuck(!entries[0]?.isIntersecting),
|
||||
{ root, threshold: 0 }
|
||||
);
|
||||
observer.observe(node);
|
||||
observerRef.current = observer;
|
||||
}, []);
|
||||
|
||||
// if these are missing for some reason, then nothing we can do. Just
|
||||
// don't render.
|
||||
// TODO: improve this display
|
||||
@@ -200,9 +167,7 @@ const DocumentsSidebar = memo(
|
||||
<div className="flex flex-col px-3 gap-6">
|
||||
{hasCited && (
|
||||
<div>
|
||||
<Header isTop onClose={closeSidebar}>
|
||||
Cited Sources
|
||||
</Header>
|
||||
<Header onClose={closeSidebar}>Cited Sources</Header>
|
||||
<ChatDocumentDisplayWrapper>
|
||||
{citedDocuments.map((document) => (
|
||||
<ChatDocumentDisplay
|
||||
@@ -221,11 +186,7 @@ const DocumentsSidebar = memo(
|
||||
|
||||
{hasOther && (
|
||||
<div>
|
||||
<div ref={moreSentinelRef} className="h-px" />
|
||||
<Header
|
||||
isTop={!hasCited || isMoreStuck}
|
||||
onClose={!hasCited || isMoreStuck ? closeSidebar : undefined}
|
||||
>
|
||||
<Header onClose={closeSidebar}>
|
||||
{citedDocuments.length > 0 ? "More" : "Found Sources"}
|
||||
</Header>
|
||||
<ChatDocumentDisplayWrapper>
|
||||
@@ -246,12 +207,7 @@ const DocumentsSidebar = memo(
|
||||
|
||||
{humanFileDescriptors && humanFileDescriptors.length > 0 && (
|
||||
<div>
|
||||
<Header
|
||||
isTop={!hasCited && !hasOther}
|
||||
onClose={!hasCited && !hasOther ? closeSidebar : undefined}
|
||||
>
|
||||
User Files
|
||||
</Header>
|
||||
<Header onClose={closeSidebar}>User Files</Header>
|
||||
<ChatDocumentDisplayWrapper>
|
||||
{humanFileDescriptors.map((file) => (
|
||||
<ChatDocumentDisplay
|
||||
|
||||
@@ -649,9 +649,9 @@ const AppInputBar = React.memo(
|
||||
<Disabled disabled>
|
||||
<Button
|
||||
icon={SvgMicrophone}
|
||||
aria-label="Configure Voice Mode"
|
||||
aria-label="Set up voice"
|
||||
prominence="tertiary"
|
||||
tooltip="Configure Voice Mode in Admin Settings."
|
||||
tooltip="Voice not configured. Set up in admin settings."
|
||||
/>
|
||||
</Disabled>
|
||||
))}
|
||||
|
||||
@@ -143,7 +143,7 @@ const LLMStepInner = ({
|
||||
rightIcon={SvgExternalLink}
|
||||
href="/admin/configuration/llm"
|
||||
>
|
||||
View in Admin Settings
|
||||
View in Admin Panel
|
||||
</Button>
|
||||
</Disabled>
|
||||
}
|
||||
|
||||
@@ -1,90 +1,55 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useSettingsContext } from "@/providers/SettingsProvider";
|
||||
import { CgArrowsExpandUpLeft } from "react-icons/cg";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import SidebarSection from "@/sections/sidebar/SidebarSection";
|
||||
import SidebarWrapper from "@/sections/sidebar/SidebarWrapper";
|
||||
import { useIsKGExposed } from "@/app/admin/kg/utils";
|
||||
import { useCustomAnalyticsEnabled } from "@/lib/hooks/useCustomAnalyticsEnabled";
|
||||
import { useUser } from "@/providers/UserProvider";
|
||||
import { UserRole } from "@/lib/types";
|
||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||
import { CombinedSettings } from "@/interfaces/settings";
|
||||
import SidebarTab from "@/refresh-components/buttons/SidebarTab";
|
||||
import SidebarBody from "@/sections/sidebar/SidebarBody";
|
||||
import InputTypeIn from "@/refresh-components/inputs/InputTypeIn";
|
||||
import { Disabled } from "@opal/core";
|
||||
import { SvgArrowUpCircle, SvgUserManage, SvgX } from "@opal/icons";
|
||||
import {
|
||||
useBillingInformation,
|
||||
useLicense,
|
||||
hasActiveSubscription,
|
||||
} from "@/lib/billing";
|
||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||
import { CombinedSettings } from "@/interfaces/settings";
|
||||
import SidebarTab from "@/refresh-components/buttons/SidebarTab";
|
||||
import SidebarBody from "@/sections/sidebar/SidebarBody";
|
||||
import {
|
||||
SvgActions,
|
||||
SvgActivity,
|
||||
SvgArrowUpCircle,
|
||||
SvgBarChart,
|
||||
SvgCpu,
|
||||
SvgFileText,
|
||||
SvgFolder,
|
||||
SvgGlobe,
|
||||
SvgArrowExchange,
|
||||
SvgImage,
|
||||
SvgKey,
|
||||
SvgOnyxLogo,
|
||||
SvgOnyxOctagon,
|
||||
SvgSearch,
|
||||
SvgServer,
|
||||
SvgSettings,
|
||||
SvgShield,
|
||||
SvgThumbsUp,
|
||||
SvgUploadCloud,
|
||||
SvgUser,
|
||||
SvgUsers,
|
||||
SvgZoomIn,
|
||||
SvgPaintBrush,
|
||||
SvgDiscordMono,
|
||||
SvgWallet,
|
||||
SvgAudio,
|
||||
} from "@opal/icons";
|
||||
import SvgMcp from "@opal/icons/mcp";
|
||||
import { ADMIN_PATHS, sidebarItem } from "@/lib/admin-routes";
|
||||
import UserAvatarPopover from "@/sections/sidebar/UserAvatarPopover";
|
||||
import { Content } from "@opal/layouts";
|
||||
import { ADMIN_ROUTES, sidebarItem } from "@/lib/admin-routes";
|
||||
import useFilter from "@/hooks/useFilter";
|
||||
import { IconFunctionComponent } from "@opal/types";
|
||||
import { Section } from "@/layouts/general-layouts";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
import { getUserDisplayName } from "@/lib/user";
|
||||
import { APP_SLOGAN } from "@/lib/constants";
|
||||
|
||||
const connectors_items = () => [
|
||||
sidebarItem(ADMIN_PATHS.INDEXING_STATUS),
|
||||
sidebarItem(ADMIN_PATHS.ADD_CONNECTOR),
|
||||
];
|
||||
const SECTIONS = {
|
||||
UNLABELED: "",
|
||||
AGENTS_AND_ACTIONS: "Agents & Actions",
|
||||
DOCUMENTS_AND_KNOWLEDGE: "Documents & Knowledge",
|
||||
INTEGRATIONS: "Integrations",
|
||||
PERMISSIONS: "Permissions",
|
||||
ORGANIZATION: "Organization",
|
||||
USAGE: "Usage",
|
||||
} as const;
|
||||
|
||||
const document_management_items = () => [
|
||||
sidebarItem(ADMIN_PATHS.DOCUMENT_SETS),
|
||||
sidebarItem(ADMIN_PATHS.DOCUMENT_EXPLORER),
|
||||
sidebarItem(ADMIN_PATHS.DOCUMENT_FEEDBACK),
|
||||
];
|
||||
interface SidebarItemEntry {
|
||||
section: string;
|
||||
name: string;
|
||||
icon: IconFunctionComponent;
|
||||
link: string;
|
||||
error?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const custom_agents_items = (isCurator: boolean, enableEnterprise: boolean) => {
|
||||
const items = [sidebarItem(ADMIN_PATHS.AGENTS)];
|
||||
|
||||
if (!isCurator) {
|
||||
items.push(
|
||||
sidebarItem(ADMIN_PATHS.SLACK_BOTS),
|
||||
sidebarItem(ADMIN_PATHS.DISCORD_BOTS)
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
sidebarItem(ADMIN_PATHS.MCP_ACTIONS),
|
||||
sidebarItem(ADMIN_PATHS.OPENAPI_ACTIONS)
|
||||
);
|
||||
|
||||
if (enableEnterprise) {
|
||||
items.push(sidebarItem(ADMIN_PATHS.STANDARD_ANSWERS));
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
const collections = (
|
||||
function buildItems(
|
||||
isCurator: boolean,
|
||||
enableCloud: boolean,
|
||||
enableEnterprise: boolean,
|
||||
@@ -92,204 +57,260 @@ const collections = (
|
||||
kgExposed: boolean,
|
||||
customAnalyticsEnabled: boolean,
|
||||
hasSubscription: boolean
|
||||
) => {
|
||||
): SidebarItemEntry[] {
|
||||
const vectorDbEnabled = settings?.settings.vector_db_enabled !== false;
|
||||
const items: SidebarItemEntry[] = [];
|
||||
|
||||
return [
|
||||
...(vectorDbEnabled
|
||||
? [
|
||||
{
|
||||
name: "Connectors",
|
||||
items: connectors_items(),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(vectorDbEnabled
|
||||
? [
|
||||
{
|
||||
name: "Document Management",
|
||||
items: document_management_items(),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: "Custom Agents",
|
||||
items: custom_agents_items(isCurator, enableEnterprise),
|
||||
},
|
||||
...(isCurator && enableEnterprise
|
||||
? [
|
||||
{
|
||||
name: "User Management",
|
||||
items: [sidebarItem(ADMIN_PATHS.GROUPS)],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(!isCurator
|
||||
? [
|
||||
{
|
||||
name: "Configuration",
|
||||
items: [
|
||||
sidebarItem(ADMIN_PATHS.CHAT_PREFERENCES),
|
||||
sidebarItem(ADMIN_PATHS.LLM_MODELS),
|
||||
sidebarItem(ADMIN_PATHS.WEB_SEARCH),
|
||||
sidebarItem(ADMIN_PATHS.IMAGE_GENERATION),
|
||||
{
|
||||
name: "Voice Mode",
|
||||
icon: SvgAudio,
|
||||
link: "/admin/configuration/voice",
|
||||
},
|
||||
sidebarItem(ADMIN_PATHS.CODE_INTERPRETER),
|
||||
...(!enableCloud && vectorDbEnabled
|
||||
? [
|
||||
{
|
||||
...sidebarItem(ADMIN_PATHS.SEARCH_SETTINGS),
|
||||
error: settings?.settings.needs_reindexing,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
sidebarItem(ADMIN_PATHS.DOCUMENT_PROCESSING),
|
||||
...(kgExposed ? [sidebarItem(ADMIN_PATHS.KNOWLEDGE_GRAPH)] : []),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "User Management",
|
||||
items: [
|
||||
...(enableEnterprise ? [sidebarItem(ADMIN_PATHS.GROUPS)] : []),
|
||||
sidebarItem(ADMIN_PATHS.API_KEYS),
|
||||
sidebarItem(ADMIN_PATHS.TOKEN_RATE_LIMITS),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Permissions",
|
||||
items: [
|
||||
sidebarItem(ADMIN_PATHS.USERS),
|
||||
...(enableEnterprise ? [sidebarItem(ADMIN_PATHS.SCIM)] : []),
|
||||
],
|
||||
},
|
||||
...(enableEnterprise
|
||||
? [
|
||||
{
|
||||
name: "Performance",
|
||||
items: [
|
||||
sidebarItem(ADMIN_PATHS.USAGE),
|
||||
...(settings?.settings.query_history_type !== "disabled"
|
||||
? [sidebarItem(ADMIN_PATHS.QUERY_HISTORY)]
|
||||
: []),
|
||||
...(!enableCloud && customAnalyticsEnabled
|
||||
? [sidebarItem(ADMIN_PATHS.CUSTOM_ANALYTICS)]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: "Settings",
|
||||
items: [
|
||||
...(enableEnterprise ? [sidebarItem(ADMIN_PATHS.THEME)] : []),
|
||||
// Always show billing/upgrade - community users need access to upgrade
|
||||
{
|
||||
...sidebarItem(ADMIN_PATHS.BILLING),
|
||||
...(hasSubscription
|
||||
? {}
|
||||
: { name: "Upgrade Plan", icon: SvgArrowUpCircle }),
|
||||
},
|
||||
...(settings?.settings.opensearch_indexing_enabled
|
||||
? [sidebarItem(ADMIN_PATHS.INDEX_MIGRATION)]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
};
|
||||
const add = (section: string, route: Parameters<typeof sidebarItem>[0]) => {
|
||||
items.push({ ...sidebarItem(route), section });
|
||||
};
|
||||
|
||||
interface AdminSidebarProps {
|
||||
// Cloud flag is passed from server component (Layout.tsx) since it's a build-time constant
|
||||
enableCloudSS: boolean;
|
||||
// Enterprise flag is also passed but we override it with runtime license check below
|
||||
enableEnterpriseSS: boolean;
|
||||
const addDisabled = (
|
||||
section: string,
|
||||
route: Parameters<typeof sidebarItem>[0],
|
||||
isDisabled: boolean
|
||||
) => {
|
||||
items.push({ ...sidebarItem(route), section, disabled: isDisabled });
|
||||
};
|
||||
|
||||
// 1. No header — core configuration (admin only)
|
||||
if (!isCurator) {
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.LLM_MODELS);
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.WEB_SEARCH);
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.IMAGE_GENERATION);
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.VOICE);
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.CODE_INTERPRETER);
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.CHAT_PREFERENCES);
|
||||
|
||||
if (vectorDbEnabled && kgExposed) {
|
||||
add(SECTIONS.UNLABELED, ADMIN_ROUTES.KNOWLEDGE_GRAPH);
|
||||
}
|
||||
|
||||
if (!enableCloud && customAnalyticsEnabled) {
|
||||
addDisabled(
|
||||
SECTIONS.UNLABELED,
|
||||
ADMIN_ROUTES.CUSTOM_ANALYTICS,
|
||||
!enableEnterprise
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Agents & Actions
|
||||
add(SECTIONS.AGENTS_AND_ACTIONS, ADMIN_ROUTES.AGENTS);
|
||||
add(SECTIONS.AGENTS_AND_ACTIONS, ADMIN_ROUTES.MCP_ACTIONS);
|
||||
add(SECTIONS.AGENTS_AND_ACTIONS, ADMIN_ROUTES.OPENAPI_ACTIONS);
|
||||
|
||||
// 3. Documents & Knowledge
|
||||
if (vectorDbEnabled) {
|
||||
add(SECTIONS.DOCUMENTS_AND_KNOWLEDGE, ADMIN_ROUTES.INDEXING_STATUS);
|
||||
add(SECTIONS.DOCUMENTS_AND_KNOWLEDGE, ADMIN_ROUTES.ADD_CONNECTOR);
|
||||
add(SECTIONS.DOCUMENTS_AND_KNOWLEDGE, ADMIN_ROUTES.DOCUMENT_SETS);
|
||||
if (!isCurator && !enableCloud) {
|
||||
items.push({
|
||||
...sidebarItem(ADMIN_ROUTES.INDEX_SETTINGS),
|
||||
section: SECTIONS.DOCUMENTS_AND_KNOWLEDGE,
|
||||
error: settings?.settings.needs_reindexing,
|
||||
});
|
||||
}
|
||||
if (!isCurator && settings?.settings.opensearch_indexing_enabled) {
|
||||
add(SECTIONS.DOCUMENTS_AND_KNOWLEDGE, ADMIN_ROUTES.INDEX_MIGRATION);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Integrations (admin only)
|
||||
if (!isCurator) {
|
||||
add(SECTIONS.INTEGRATIONS, ADMIN_ROUTES.API_KEYS);
|
||||
add(SECTIONS.INTEGRATIONS, ADMIN_ROUTES.SLACK_BOTS);
|
||||
add(SECTIONS.INTEGRATIONS, ADMIN_ROUTES.DISCORD_BOTS);
|
||||
}
|
||||
|
||||
// 5. Permissions
|
||||
if (!isCurator) {
|
||||
add(SECTIONS.PERMISSIONS, ADMIN_ROUTES.USERS);
|
||||
addDisabled(SECTIONS.PERMISSIONS, ADMIN_ROUTES.GROUPS, !enableEnterprise);
|
||||
addDisabled(SECTIONS.PERMISSIONS, ADMIN_ROUTES.SCIM, !enableEnterprise);
|
||||
} else if (enableEnterprise) {
|
||||
add(SECTIONS.PERMISSIONS, ADMIN_ROUTES.GROUPS);
|
||||
}
|
||||
|
||||
// 6. Organization (admin only)
|
||||
if (!isCurator) {
|
||||
if (hasSubscription) {
|
||||
add(SECTIONS.ORGANIZATION, ADMIN_ROUTES.BILLING);
|
||||
} else {
|
||||
items.push({
|
||||
section: SECTIONS.ORGANIZATION,
|
||||
name: "Upgrade Plan",
|
||||
icon: SvgArrowUpCircle,
|
||||
link: ADMIN_ROUTES.BILLING.path,
|
||||
});
|
||||
}
|
||||
add(SECTIONS.ORGANIZATION, ADMIN_ROUTES.TOKEN_RATE_LIMITS);
|
||||
addDisabled(SECTIONS.ORGANIZATION, ADMIN_ROUTES.THEME, !enableEnterprise);
|
||||
}
|
||||
|
||||
// 7. Usage (admin only)
|
||||
if (!isCurator) {
|
||||
addDisabled(SECTIONS.USAGE, ADMIN_ROUTES.USAGE, !enableEnterprise);
|
||||
if (settings?.settings.query_history_type !== "disabled") {
|
||||
addDisabled(
|
||||
SECTIONS.USAGE,
|
||||
ADMIN_ROUTES.QUERY_HISTORY,
|
||||
!enableEnterprise
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export default function AdminSidebar({
|
||||
enableCloudSS,
|
||||
enableEnterpriseSS,
|
||||
}: AdminSidebarProps) {
|
||||
/** Preserve section ordering while grouping consecutive items by section. */
|
||||
function groupBySection(items: SidebarItemEntry[]) {
|
||||
const groups: { section: string; items: SidebarItemEntry[] }[] = [];
|
||||
for (const item of items) {
|
||||
const last = groups[groups.length - 1];
|
||||
if (last && last.section === item.section) {
|
||||
last.items.push(item);
|
||||
} else {
|
||||
groups.push({ section: item.section, items: [item] });
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
interface AdminSidebarProps {
|
||||
enableCloudSS: boolean;
|
||||
}
|
||||
|
||||
export default function AdminSidebar({ enableCloudSS }: AdminSidebarProps) {
|
||||
const { kgExposed } = useIsKGExposed();
|
||||
const pathname = usePathname();
|
||||
const { customAnalyticsEnabled } = useCustomAnalyticsEnabled();
|
||||
const { user } = useUser();
|
||||
const settings = useSettingsContext();
|
||||
const { data: billingData } = useBillingInformation();
|
||||
const { data: licenseData } = useLicense();
|
||||
|
||||
// Use runtime license check for enterprise features
|
||||
// This checks settings.ee_features_enabled (set by backend based on license status)
|
||||
// Falls back to build-time check if LICENSE_ENFORCEMENT_ENABLED=false
|
||||
const enableEnterprise = usePaidEnterpriseFeaturesEnabled();
|
||||
|
||||
const { data: billingData, isLoading: billingLoading } =
|
||||
useBillingInformation();
|
||||
const { data: licenseData, isLoading: licenseLoading } = useLicense();
|
||||
const isCurator =
|
||||
user?.role === UserRole.CURATOR || user?.role === UserRole.GLOBAL_CURATOR;
|
||||
// Default to true while loading to avoid flashing "Upgrade Plan"
|
||||
const hasSubscriptionOrLicense =
|
||||
billingLoading || licenseLoading
|
||||
? true
|
||||
: Boolean(
|
||||
(billingData && hasActiveSubscription(billingData)) ||
|
||||
licenseData?.has_license
|
||||
);
|
||||
|
||||
// Check if user has an active subscription or license for billing link text
|
||||
// Show "Plans & Billing" if they have either (even if Stripe connection fails)
|
||||
const hasSubscription = Boolean(
|
||||
(billingData && hasActiveSubscription(billingData)) ||
|
||||
licenseData?.has_license
|
||||
);
|
||||
|
||||
const items = collections(
|
||||
const allItems = buildItems(
|
||||
isCurator,
|
||||
enableCloudSS,
|
||||
enableEnterprise,
|
||||
settings,
|
||||
kgExposed,
|
||||
customAnalyticsEnabled,
|
||||
hasSubscription
|
||||
hasSubscriptionOrLicense
|
||||
);
|
||||
|
||||
const itemExtractor = useCallback((item: SidebarItemEntry) => item.name, []);
|
||||
|
||||
const { query, setQuery, filtered } = useFilter(allItems, itemExtractor);
|
||||
|
||||
const groups = groupBySection(filtered);
|
||||
|
||||
return (
|
||||
<SidebarWrapper>
|
||||
<SidebarBody
|
||||
scrollKey="admin-sidebar"
|
||||
actionButtons={
|
||||
<SidebarTab
|
||||
icon={({ className }) => (
|
||||
<CgArrowsExpandUpLeft className={className} size={16} />
|
||||
)}
|
||||
href="/app"
|
||||
>
|
||||
Exit Admin
|
||||
</SidebarTab>
|
||||
}
|
||||
footer={
|
||||
<div className="flex flex-col gap-2">
|
||||
{settings.webVersion && (
|
||||
<Text as="p" text02 secondaryBody className="px-2">
|
||||
{`Onyx version: ${settings.webVersion}`}
|
||||
</Text>
|
||||
)}
|
||||
<UserAvatarPopover />
|
||||
<div className="flex flex-col w-full">
|
||||
<SidebarTab
|
||||
icon={({ className }) => <SvgX className={className} size={16} />}
|
||||
href="/app"
|
||||
lowlight
|
||||
>
|
||||
Exit Admin Panel
|
||||
</SidebarTab>
|
||||
<InputTypeIn
|
||||
variant="internal"
|
||||
leftSearchIcon
|
||||
placeholder="Search..."
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
footer={
|
||||
<Section gap={0} height="fit" alignItems="start">
|
||||
<div className="p-[0.38rem] w-full">
|
||||
<Content
|
||||
icon={SvgUserManage}
|
||||
title={getUserDisplayName(user)}
|
||||
sizePreset="main-ui"
|
||||
variant="body"
|
||||
prominence="muted"
|
||||
widthVariant="full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row gap-1 p-[0.38rem] w-full">
|
||||
<Text text03 secondaryAction>
|
||||
<a
|
||||
className="underline"
|
||||
href="https://onyx.app"
|
||||
target="_blank"
|
||||
>
|
||||
Onyx
|
||||
</a>
|
||||
</Text>
|
||||
<Text text03 secondaryBody>
|
||||
|
|
||||
</Text>
|
||||
{settings.webVersion ? (
|
||||
<Text text03 secondaryBody>
|
||||
{settings.webVersion}
|
||||
</Text>
|
||||
) : (
|
||||
<Text text03 secondaryBody>
|
||||
{APP_SLOGAN}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</Section>
|
||||
}
|
||||
>
|
||||
{items.map((collection, index) => (
|
||||
<SidebarSection key={index} title={collection.name}>
|
||||
<div className="flex flex-col w-full">
|
||||
{collection.items.map(({ link, icon: Icon, name }, index) => (
|
||||
{groups.map((group, groupIndex) => {
|
||||
const tabs = group.items.map(({ link, icon, name, disabled }) => (
|
||||
<Disabled key={link} disabled={disabled}>
|
||||
{/*
|
||||
# NOTE (@raunakab)
|
||||
We intentionally add a `div` intermediary here.
|
||||
Without it, the disabled styling that is default provided by the `Disabled` component (which we want here) would be overridden by the custom disabled styling provided by the `SidebarTab`.
|
||||
Therefore, in order to avoid that overriding, we add a layer of indirection.
|
||||
*/}
|
||||
<div>
|
||||
<SidebarTab
|
||||
key={index}
|
||||
href={link}
|
||||
selected={pathname.startsWith(link)}
|
||||
icon={({ className }) => (
|
||||
<Icon className={className} size={16} />
|
||||
)}
|
||||
lowlight={disabled}
|
||||
icon={icon}
|
||||
href={disabled ? undefined : link}
|
||||
selected={!disabled && pathname.startsWith(link)}
|
||||
>
|
||||
{name}
|
||||
</SidebarTab>
|
||||
))}
|
||||
</div>
|
||||
</SidebarSection>
|
||||
))}
|
||||
</div>
|
||||
</Disabled>
|
||||
));
|
||||
|
||||
if (!group.section) {
|
||||
return <div key={groupIndex}>{tabs}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarSection key={groupIndex} title={group.section}>
|
||||
{tabs}
|
||||
</SidebarSection>
|
||||
);
|
||||
})}
|
||||
</SidebarBody>
|
||||
</SidebarWrapper>
|
||||
);
|
||||
|
||||
@@ -613,7 +613,7 @@ const MemoizedAppSidebarInner = memo(
|
||||
icon={SvgSettings}
|
||||
folded={folded}
|
||||
>
|
||||
{isAdmin ? "Admin Settings" : "Curator Panel"}
|
||||
{isAdmin ? "Admin Panel" : "Curator Panel"}
|
||||
</SidebarTab>
|
||||
)}
|
||||
<UserAvatarPopover
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function EmbeddingSidebar() {
|
||||
|
||||
return (
|
||||
<StepSidebar
|
||||
buttonName="Search Settings"
|
||||
buttonName="Index Settings"
|
||||
buttonIcon={SvgSettings}
|
||||
buttonHref="/admin/configuration/search"
|
||||
>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { ANONYMOUS_USER_NAME, LOGOUT_DISABLED } from "@/lib/constants";
|
||||
import { LOGOUT_DISABLED } from "@/lib/constants";
|
||||
import { Notification } from "@/interfaces/settings";
|
||||
import useSWR, { preload } from "swr";
|
||||
import { errorHandlingFetcher } from "@/lib/fetcher";
|
||||
import { checkUserIsNoAuthUser, logout } from "@/lib/user";
|
||||
import { checkUserIsNoAuthUser, getUserDisplayName, logout } from "@/lib/user";
|
||||
import { useUser } from "@/providers/UserProvider";
|
||||
import InputAvatar from "@/refresh-components/inputs/InputAvatar";
|
||||
import Text from "@/refresh-components/texts/Text";
|
||||
@@ -27,20 +27,6 @@ import { toast } from "@/hooks/useToast";
|
||||
import useAppFocus from "@/hooks/useAppFocus";
|
||||
import { useVectorDbEnabled } from "@/providers/SettingsProvider";
|
||||
|
||||
function getDisplayName(email?: string, personalName?: string): string {
|
||||
// Prioritize custom personal name if set
|
||||
if (personalName && personalName.trim()) {
|
||||
return personalName.trim();
|
||||
}
|
||||
|
||||
// Fallback to email-derived username
|
||||
if (!email) return ANONYMOUS_USER_NAME;
|
||||
const atIndex = email.indexOf("@");
|
||||
if (atIndex <= 0) return ANONYMOUS_USER_NAME;
|
||||
|
||||
return email.substring(0, atIndex);
|
||||
}
|
||||
|
||||
interface SettingsPopoverProps {
|
||||
onUserSettingsClick: () => void;
|
||||
onOpenNotifications: () => void;
|
||||
@@ -175,7 +161,7 @@ export default function UserAvatarPopover({
|
||||
errorHandlingFetcher
|
||||
);
|
||||
|
||||
const displayName = getDisplayName(user?.email, user?.personalization?.name);
|
||||
const userDisplayName = getUserDisplayName(user);
|
||||
const undismissedCount =
|
||||
notifications?.filter((n) => !n.dismissed).length ?? 0;
|
||||
const hasNotifications = undismissedCount > 0;
|
||||
@@ -209,7 +195,7 @@ export default function UserAvatarPopover({
|
||||
)}
|
||||
>
|
||||
<Text as="p" inverted secondaryBody>
|
||||
{displayName[0]?.toUpperCase()}
|
||||
{userDisplayName[0]?.toUpperCase()}
|
||||
</Text>
|
||||
</InputAvatar>
|
||||
)}
|
||||
@@ -231,7 +217,7 @@ export default function UserAvatarPopover({
|
||||
// Specifying a dummy `onClick` handler solves that.
|
||||
onClick={() => undefined}
|
||||
>
|
||||
{displayName}
|
||||
{userDisplayName}
|
||||
</SidebarTab>
|
||||
</div>
|
||||
</Popover.Trigger>
|
||||
|
||||
@@ -52,9 +52,9 @@ const ADMIN_PAGES: AdminPageSnapshot[] = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Custom Agents - Slack Bots",
|
||||
name: "Integrations - Slack Integration",
|
||||
path: "bots",
|
||||
pageTitle: "Slack Bots",
|
||||
pageTitle: "Slack Integration",
|
||||
options: {
|
||||
paragraphText:
|
||||
"Setup Slack bots that connect to Onyx. Once setup, you will be able to ask questions to Onyx directly from Slack. Additionally, you can:",
|
||||
@@ -96,9 +96,9 @@ const ADMIN_PAGES: AdminPageSnapshot[] = [
|
||||
pageTitle: "Appearance & Theming",
|
||||
},
|
||||
{
|
||||
name: "Configuration - Search Settings",
|
||||
name: "Documents & Knowledge - Index Settings",
|
||||
path: "configuration/search",
|
||||
pageTitle: "Search Settings",
|
||||
pageTitle: "Index Settings",
|
||||
},
|
||||
{
|
||||
name: "Custom Agents - MCP Actions",
|
||||
@@ -111,9 +111,9 @@ const ADMIN_PAGES: AdminPageSnapshot[] = [
|
||||
pageTitle: "OpenAPI Actions",
|
||||
},
|
||||
{
|
||||
name: "User Management - Token Rate Limits",
|
||||
name: "Organization - Spending Limits",
|
||||
path: "token-rate-limits",
|
||||
pageTitle: "Token Rate Limits",
|
||||
pageTitle: "Spending Limits",
|
||||
options: {
|
||||
paragraphText:
|
||||
"Token rate limits enable you control how many tokens can be spent in a given time period. With token rate limits, you can:",
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe("Admin Workflow E2E Flows", () => {
|
||||
await expect(
|
||||
adminPage
|
||||
.locator('[aria-label="admin-page-title"]')
|
||||
.getByText("Discord Bots")
|
||||
.getByText("Discord Integration")
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
adminPage.locator("text=Server Configurations").first()
|
||||
|
||||
@@ -26,7 +26,7 @@ test.describe("Bot Configuration Page", () => {
|
||||
await expect(
|
||||
adminPage
|
||||
.locator('[aria-label="admin-page-title"]')
|
||||
.getByText("Discord Bots")
|
||||
.getByText("Discord Integration")
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
@@ -304,7 +304,9 @@ export async function gotoDiscordBotPage(adminPage: Page): Promise<void> {
|
||||
await adminPage.goto("/admin/discord-bot");
|
||||
await adminPage.waitForLoadState("networkidle");
|
||||
// Wait for the page title
|
||||
await adminPage.waitForSelector("text=Discord Bots", { timeout: 15000 });
|
||||
await adminPage.waitForSelector("text=Discord Integration", {
|
||||
timeout: 15000,
|
||||
});
|
||||
}
|
||||
|
||||
export async function gotoGuildDetailPage(
|
||||
|
||||
@@ -289,7 +289,7 @@ test.describe("Guilds List Page", () => {
|
||||
await expect(
|
||||
adminPage
|
||||
.locator('[aria-label="admin-page-title"]')
|
||||
.getByText("Discord Bots")
|
||||
.getByText("Discord Integration")
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user