Skip to content

Commit 8e7ca46

Browse files
Wevesgreptile-apps[bot]
authored andcommitted
Single source of truth for image capability (onyx-dot-app#4612)
* Single source of truth for image capability * Update web/src/app/admin/assistants/AssistantEditor.tsx Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Fix tests --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 6b38700 commit 8e7ca46

File tree

11 files changed

+91
-113
lines changed

11 files changed

+91
-113
lines changed

backend/alembic/versions/47a07e1a38f1_fix_invalid_model_configurations_state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
depends_on = None
2525

2626

27-
class ModelConfiguration(BaseModel):
27+
class _SimpleModelConfiguration(BaseModel):
2828
# Configure model to read from attributes
2929
model_config = ConfigDict(from_attributes=True)
3030

@@ -82,7 +82,7 @@ def upgrade() -> None:
8282
)
8383

8484
model_configurations = [
85-
ModelConfiguration.model_validate(model_configuration)
85+
_SimpleModelConfiguration.model_validate(model_configuration)
8686
for model_configuration in connection.execute(
8787
sa.select(
8888
model_configuration_table.c.id,

backend/onyx/server/manage/llm/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic import Field
55

66
from onyx.llm.utils import get_max_input_tokens
7+
from onyx.llm.utils import model_supports_image_input
78

89

910
if TYPE_CHECKING:
@@ -152,6 +153,7 @@ class ModelConfigurationView(BaseModel):
152153
name: str
153154
is_visible: bool | None = False
154155
max_input_tokens: int | None = None
156+
supports_image_input: bool
155157

156158
@classmethod
157159
def from_model(
@@ -166,6 +168,10 @@ def from_model(
166168
or get_max_input_tokens(
167169
model_name=model_configuration_model.name, model_provider=provider_name
168170
),
171+
supports_image_input=model_supports_image_input(
172+
model_name=model_configuration_model.name,
173+
model_provider=provider_name,
174+
),
169175
)
170176

171177

backend/tests/integration/tests/llm_provider/test_llm_provider.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import uuid
2+
from typing import Any
23

34
import pytest
45
import requests
56
from requests.models import Response
67

78
from onyx.llm.utils import get_max_input_tokens
9+
from onyx.llm.utils import model_supports_image_input
810
from onyx.server.manage.llm.models import ModelConfigurationUpsertRequest
911
from tests.integration.common_utils.constants import API_SERVER_URL
1012
from tests.integration.common_utils.managers.user import UserManager
1113
from tests.integration.common_utils.test_models import DATestUser
1214

1315

14-
_DEFAULT_MODELS = ["gpt-4", "gpt-4o"]
15-
16-
1716
def _get_provider_by_id(admin_user: DATestUser, provider_id: str) -> dict | None:
1817
"""Utility function to fetch an LLM provider by ID"""
1918
response = requests.get(
@@ -40,24 +39,32 @@ def assert_response_is_equivalent(
4039

4140
assert provider_data["default_model_name"] == default_model_name
4241

43-
def fill_max_input_tokens_if_none(
42+
def fill_max_input_tokens_and_supports_image_input(
4443
req: ModelConfigurationUpsertRequest,
45-
) -> ModelConfigurationUpsertRequest:
46-
return ModelConfigurationUpsertRequest(
44+
) -> dict[str, Any]:
45+
filled_with_max_input_tokens = ModelConfigurationUpsertRequest(
4746
name=req.name,
4847
is_visible=req.is_visible,
4948
max_input_tokens=req.max_input_tokens
5049
or get_max_input_tokens(
5150
model_name=req.name, model_provider=default_model_name
5251
),
5352
)
53+
return {
54+
**filled_with_max_input_tokens.model_dump(),
55+
"supports_image_input": model_supports_image_input(
56+
req.name, created_provider["provider"]
57+
),
58+
}
5459

5560
actual = set(
5661
tuple(model_configuration.items())
5762
for model_configuration in provider_data["model_configurations"]
5863
)
5964
expected = set(
60-
tuple(fill_max_input_tokens_if_none(model_configuration).dict().items())
65+
tuple(
66+
fill_max_input_tokens_and_supports_image_input(model_configuration).items()
67+
)
6168
for model_configuration in model_configurations
6269
)
6370
assert actual == expected
@@ -150,7 +157,7 @@ def test_create_llm_provider(
150157
"api_key": "sk-000000000000000000000000000000000000000000000000",
151158
"default_model_name": default_model_name,
152159
"model_configurations": [
153-
model_configuration.dict()
160+
model_configuration.model_dump()
154161
for model_configuration in model_configurations
155162
],
156163
"is_public": True,

web/src/app/admin/assistants/AssistantEditor.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ import { getDisplayNameForModel, useLabels } from "@/lib/hooks";
2525
import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable";
2626
import { addAssistantToList } from "@/lib/assistants/updateAssistantPreferences";
2727
import {
28-
checkLLMSupportsImageInput,
2928
destructureValue,
29+
modelSupportsImageInput,
3030
structureValue,
3131
} from "@/lib/llm/utils";
3232
import { ToolSnapshot } from "@/lib/tools/interfaces";
@@ -139,6 +139,7 @@ export function AssistantEditor({
139139
admin?: boolean;
140140
}) {
141141
const { refreshAssistants, isImageGenerationAvailable } = useAssistants();
142+
142143
const router = useRouter();
143144
const searchParams = useSearchParams();
144145
const isAdminPage = searchParams?.get("admin") === "true";
@@ -643,7 +644,8 @@ export function AssistantEditor({
643644

644645
// model must support image input for image generation
645646
// to work
646-
const currentLLMSupportsImageOutput = checkLLMSupportsImageInput(
647+
const currentLLMSupportsImageOutput = modelSupportsImageInput(
648+
llmProviders,
647649
values.llm_model_version_override || defaultModelName || ""
648650
);
649651

web/src/app/admin/configuration/llm/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export interface ModelConfiguration {
7171
name: string;
7272
is_visible: boolean;
7373
max_input_tokens: number | null;
74+
supports_image_input: boolean;
7475
}
7576

7677
export interface VisionProvider extends LLMProviderView {

web/src/app/chat/ChatPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ import { buildFilters } from "@/lib/search/utils";
9090
import { SettingsContext } from "@/components/settings/SettingsProvider";
9191
import Dropzone from "react-dropzone";
9292
import {
93-
checkLLMSupportsImageInput,
9493
getFinalLLM,
94+
modelSupportsImageInput,
9595
structureValue,
9696
} from "@/lib/llm/utils";
9797
import { ChatInputBar } from "./input/ChatInputBar";
@@ -1952,7 +1952,7 @@ export function ChatPage({
19521952
liveAssistant,
19531953
llmManager.currentLlm
19541954
);
1955-
const llmAcceptsImages = checkLLMSupportsImageInput(llmModel);
1955+
const llmAcceptsImages = modelSupportsImageInput(llmProviders, llmModel);
19561956

19571957
const imageFiles = acceptedFiles.filter((file) =>
19581958
file.type.startsWith("image/")

web/src/app/chat/input/LLMPopover.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "@/components/ui/popover";
77
import { getDisplayNameForModel } from "@/lib/hooks";
88
import {
9-
checkLLMSupportsImageInput,
9+
modelSupportsImageInput,
1010
destructureValue,
1111
structureValue,
1212
} from "@/lib/llm/utils";
@@ -175,7 +175,10 @@ export default function LLMPopover({
175175
>
176176
<div className="flex-grow max-h-[300px] default-scrollbar overflow-y-auto">
177177
{llmOptions.map(({ name, icon, value }, index) => {
178-
if (!requiresImageGeneration || checkLLMSupportsImageInput(name)) {
178+
if (
179+
!requiresImageGeneration ||
180+
modelSupportsImageInput(llmProviders, name)
181+
) {
179182
return (
180183
<button
181184
key={index}
@@ -206,7 +209,7 @@ export default function LLMPopover({
206209
}
207210
})()}
208211
{llmManager.imageFilesPresent &&
209-
!checkLLMSupportsImageInput(name) && (
212+
!modelSupportsImageInput(llmProviders, name) && (
210213
<TooltipProvider>
211214
<Tooltip delayDuration={0}>
212215
<TooltipTrigger className="my-auto flex items-center ml-auto">

web/src/components/llm/LLMSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from "react";
22
import { getDisplayNameForModel } from "@/lib/hooks";
33
import {
4-
checkLLMSupportsImageInput,
54
destructureValue,
5+
modelSupportsImageInput,
66
structureValue,
77
} from "@/lib/llm/utils";
88
import {
@@ -96,7 +96,7 @@ export const LLMSelector: React.FC<LLMSelectorProps> = ({
9696
{llmOptions.map((option) => {
9797
if (
9898
!requiresImageGeneration ||
99-
checkLLMSupportsImageInput(option.name)
99+
modelSupportsImageInput(llmProviders, option.name)
100100
) {
101101
return (
102102
<SelectItem key={option.value} value={option.value}>

web/src/lib/chat/fetchAssistantdata.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { fetchSS } from "@/lib/utilsSS";
22
import { Persona } from "@/app/admin/assistants/interfaces";
33
import { fetchLLMProvidersSS } from "@/lib/llm/fetchLLMs";
44
import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS";
5-
import { checkLLMSupportsImageInput } from "../llm/utils";
5+
import { modelSupportsImageInput } from "../llm/utils";
66
import { filterAssistants } from "../assistants/utils";
77

88
interface AssistantData {
@@ -47,7 +47,7 @@ export async function fetchAssistantData(): Promise<AssistantData> {
4747
(provider) =>
4848
provider.provider === "openai" ||
4949
provider.model_configurations.some((modelConfiguration) =>
50-
checkLLMSupportsImageInput(modelConfiguration.name)
50+
modelSupportsImageInput(llmProviders, modelConfiguration.name)
5151
)
5252
);
5353

web/src/lib/chat/fetchSomeChatData.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
import { hasCompletedWelcomeFlowSS } from "@/components/initialSetup/welcome/WelcomeModalWrapper";
2626
import { fetchAssistantsSS } from "../assistants/fetchAssistantsSS";
2727
import { NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN } from "../constants";
28-
import { checkLLMSupportsImageInput } from "../llm/utils";
2928

3029
interface FetchChatDataResult {
3130
user?: User | null;
@@ -173,8 +172,8 @@ export async function fetchSomeChatData(
173172
const hasImageCompatibleModel = result.llmProviders?.some(
174173
(provider) =>
175174
provider.provider === "openai" ||
176-
provider.model_configurations.some((modelConfiguration) =>
177-
checkLLMSupportsImageInput(modelConfiguration.name)
175+
provider.model_configurations.some(
176+
(modelConfiguration) => modelConfiguration.supports_image_input
178177
)
179178
);
180179

0 commit comments

Comments
 (0)