Skip to content

Commit 90917b8

Browse files
onyx-cherry-pick[bot]jmelahmanclaude
authored
fix(web): show the container host note on all self-hosted providers (#13858) to release v4.5 (#13867)
Co-authored-by: Jamison Lahman <jamison@lahman.dev> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent c68ceb3 commit 90917b8

5 files changed

Lines changed: 41 additions & 26 deletions

File tree

web/src/sections/modals/languageModels/CustomModal.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
DisplayNameField,
1919
ModelAccessField,
2020
ModalWrapper,
21+
useApiBaseSubDescription,
2122
} from "@/sections/modals/languageModels/shared";
2223
import { useCustomProviderNames } from "@/lib/languageModels/hooks";
2324
import InputTypeInField from "@/refresh-components/form/InputTypeInField";
@@ -245,6 +246,7 @@ export default function CustomModal({
245246
}: LLMProviderFormProps) {
246247
const isOnboarding = variant === "onboarding";
247248
const { mutate } = useSWRConfig();
249+
const apiBaseSubDescription = useApiBaseSubDescription();
248250

249251
const onClose = () => onOpenChange?.(false);
250252

@@ -393,7 +395,7 @@ export default function CustomModal({
393395
subDescription="Paste your API key if your model provider requires authentication."
394396
/>
395397

396-
<APIBaseField optional />
398+
<APIBaseField optional subDescription={apiBaseSubDescription} />
397399

398400
<InputPadder>
399401
<InputVertical

web/src/sections/modals/languageModels/LMStudioModal.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { useSWRConfig } from "swr";
44
import { useFormikContext } from "formik";
55
import { InputDivider, toast } from "@opal/layouts";
6-
import { markdown } from "@opal/utils";
76
import {
87
LLMProviderFormProps,
98
LLMProviderName,
@@ -20,11 +19,11 @@ import { LLMProviderConfiguredSource } from "@/lib/analytics/utils";
2019
import {
2120
APIKeyField,
2221
APIBaseField,
23-
CONTAINERIZED_HOST_NOTE,
2422
ModelSelectionField,
2523
DisplayNameField,
2624
ModelAccessField,
2725
ModalWrapper,
26+
useApiBaseSubDescription,
2827
} from "@/sections/modals/languageModels/shared";
2928
import { fetchModels } from "@/lib/languageModels/svc";
3029
import { refreshLlmProviderCaches } from "@/lib/languageModels/cache";
@@ -47,7 +46,9 @@ function LMStudioModalInternals({
4746
isOnboarding,
4847
}: LMStudioModalInternalsProps) {
4948
const formikProps = useFormikContext<LMStudioModalValues>();
50-
const settings = useSettings();
49+
const apiBaseSubDescription = useApiBaseSubDescription(
50+
"The base URL for your LM Studio server."
51+
);
5152

5253
const isFetchDisabled = !formikProps.values.api_base;
5354

@@ -75,13 +76,7 @@ function LMStudioModalInternals({
7576
return (
7677
<>
7778
<APIBaseField
78-
subDescription={
79-
settings.is_containerized
80-
? markdown(
81-
`The base URL for your LM Studio server. ${CONTAINERIZED_HOST_NOTE}`
82-
)
83-
: "The base URL for your LM Studio server."
84-
}
79+
subDescription={apiBaseSubDescription}
8580
placeholder="Your LM Studio API base URL"
8681
/>
8782

web/src/sections/modals/languageModels/OllamaModal.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Dispatch, SetStateAction, useMemo, useState } from "react";
44
import { useSWRConfig } from "swr";
55
import { useFormikContext } from "formik";
66
import { InputDivider, InputVertical, toast } from "@opal/layouts";
7-
import { markdown } from "@opal/utils";
87
import PasswordInputTypeInField from "@/refresh-components/form/PasswordInputTypeInField";
98
import {
109
LLMProviderFormProps,
@@ -20,11 +19,11 @@ import {
2019
import { submitProvider } from "@/sections/modals/languageModels/svc";
2120
import { LLMProviderConfiguredSource } from "@/lib/analytics/utils";
2221
import {
23-
CONTAINERIZED_HOST_NOTE,
2422
ModelSelectionField,
2523
DisplayNameField,
2624
ModelAccessField,
2725
ModalWrapper,
26+
useApiBaseSubDescription,
2827
} from "@/sections/modals/languageModels/shared";
2928
import { fetchOllamaModels } from "@/lib/languageModels/svc";
3029
import { Card, Tabs } from "@opal/components";
@@ -56,7 +55,9 @@ function OllamaModalInternals({
5655
setTab,
5756
}: OllamaModalInternalsProps) {
5857
const formikProps = useFormikContext<OllamaModalValues>();
59-
const settings = useSettings();
58+
const apiBaseSubDescription = useApiBaseSubDescription(
59+
"The base URL for your Ollama instance."
60+
);
6061

6162
const isFetchDisabled = useMemo(
6263
() =>
@@ -104,13 +105,7 @@ function OllamaModalInternals({
104105
<InputVertical
105106
withLabel="api_base"
106107
title="API Base URL"
107-
subDescription={
108-
settings.is_containerized
109-
? markdown(
110-
`The base URL for your Ollama instance. ${CONTAINERIZED_HOST_NOTE}`
111-
)
112-
: "The base URL for your Ollama instance."
113-
}
108+
subDescription={apiBaseSubDescription}
114109
>
115110
<InputTypeInField
116111
name="api_base"

web/src/sections/modals/languageModels/OpenAICompatibleModal.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use client";
22

3-
import { markdown } from "@opal/utils";
43
import { useSWRConfig } from "swr";
54
import { useFormikContext } from "formik";
65
import { InputDivider, toast } from "@opal/layouts";
@@ -25,6 +24,7 @@ import {
2524
DisplayNameField,
2625
ModelAccessField,
2726
ModalWrapper,
27+
useApiBaseSubDescription,
2828
} from "@/sections/modals/languageModels/shared";
2929
import { refreshLlmProviderCaches } from "@/lib/languageModels/cache";
3030

@@ -43,6 +43,10 @@ function OpenAICompatibleModalInternals({
4343
isOnboarding,
4444
}: OpenAICompatibleModalInternalsProps) {
4545
const formikProps = useFormikContext<OpenAICompatibleModalValues>();
46+
const apiBaseSubDescription = useApiBaseSubDescription(
47+
"Paste your OpenAI-compatible endpoint URL.",
48+
"[Learn More](https://docs.litellm.ai/docs/providers/openai_compatible)"
49+
);
4650

4751
const isFetchDisabled = !formikProps.values.api_base;
4852

@@ -67,9 +71,7 @@ function OpenAICompatibleModalInternals({
6771
return (
6872
<>
6973
<APIBaseField
70-
subDescription={markdown(
71-
"Paste your OpenAI-compatible endpoint URL. [Learn More](https://docs.litellm.ai/docs/providers/openai_compatible)"
72-
)}
74+
subDescription={apiBaseSubDescription}
7375
placeholder="http://localhost:8000/v1"
7476
/>
7577

web/src/sections/modals/languageModels/shared.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import useUsers from "@/hooks/useUsers";
5555
import { UserRole } from "@/lib/types";
5656
import { Modal } from "@opal/components";
5757
import { getProvider } from "@/lib/languageModels";
58+
import { useSettings } from "@/lib/settings/hooks";
5859

5960
// ─── DisplayNameField ────────────────────────────────────────────────────────
6061

@@ -124,7 +125,27 @@ export function APIKeyField({
124125
* `host.docker.internal`.
125126
*/
126127
export const CONTAINERIZED_HOST_NOTE =
127-
"With Onyx running in a container, `host.docker.internal` acts like `localhost` inside the container.";
128+
"With Onyx running in a container, use `host.docker.internal` in place of `localhost` to reach a service on your host.";
129+
130+
/**
131+
* Builds the API Base URL `subDescription` for self-hosted and custom
132+
* providers. These point at a service on the admin's own machine, which
133+
* `localhost` does not reach from inside a container — so when Onyx is
134+
* containerized, {@link CONTAINERIZED_HOST_NOTE} goes between `description`
135+
* and `suffix`.
136+
*/
137+
export function useApiBaseSubDescription(
138+
description?: string,
139+
suffix?: string
140+
): RichStr | undefined {
141+
const settings = useSettings();
142+
const sentences = [
143+
description,
144+
settings.is_containerized ? CONTAINERIZED_HOST_NOTE : undefined,
145+
suffix,
146+
].filter((sentence) => sentence !== undefined);
147+
return sentences.length > 0 ? markdown(sentences.join(" ")) : undefined;
148+
}
128149

129150
export interface APIBaseFieldProps {
130151
optional?: boolean;

0 commit comments

Comments
 (0)