mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(dashboard): don't let a provider-name query hide the selected provider's models (#65374) (#65413)
Co-authored-by: Simplicio, Wesley (ext) <wesley.simplicio.ext@siemens-energy.com>
This commit is contained in:
parent
b1fb3c5285
commit
07ba9e9266
3 changed files with 79 additions and 2 deletions
|
|
@ -11,6 +11,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||
import { createPortal } from "react-dom";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
import { fuzzyRank } from "@/lib/fuzzy";
|
||||
import { queryMatchesProviderOnly } from "@/lib/model-picker-filter";
|
||||
|
||||
/**
|
||||
* Two-stage model picker modal.
|
||||
|
|
@ -226,15 +227,30 @@ export function ModelPickerDialog(props: Props) {
|
|||
[providers, trimmedQuery],
|
||||
);
|
||||
|
||||
// A query that matched the SELECTED provider by name/slug (not its models)
|
||||
// located that provider — it shouldn't also hide that provider's models
|
||||
// just because their ids don't share a substring with the provider name
|
||||
// (e.g. typing "aws" to find "AWS Build" then finding zero of its Claude
|
||||
// model ids contain "aws"). Fall back to an unfiltered model list in that
|
||||
// case; a query that also matches a model id keeps filtering normally.
|
||||
const queryMatchesSelectedProviderOnly = useMemo(
|
||||
() => queryMatchesProviderOnly(selectedProvider, models, trimmedQuery),
|
||||
[trimmedQuery, selectedProvider, models],
|
||||
);
|
||||
|
||||
// Fuzzy-ranked models carrying the matched character positions so the model
|
||||
// list can highlight why each entry matched.
|
||||
const filteredModels = useMemo(
|
||||
() =>
|
||||
fuzzyRank(models, trimmedQuery, (m) => m).map((r) => ({
|
||||
fuzzyRank(
|
||||
models,
|
||||
queryMatchesSelectedProviderOnly ? "" : trimmedQuery,
|
||||
(m) => m,
|
||||
).map((r) => ({
|
||||
model: r.item,
|
||||
positions: r.positions,
|
||||
})),
|
||||
[models, trimmedQuery],
|
||||
[models, trimmedQuery, queryMatchesSelectedProviderOnly],
|
||||
);
|
||||
|
||||
const canConfirm = !!selectedProvider && !!selectedModel && !applying;
|
||||
|
|
|
|||
38
web/src/lib/model-picker-filter.test.ts
Normal file
38
web/src/lib/model-picker-filter.test.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { queryMatchesProviderOnly } from "./model-picker-filter";
|
||||
|
||||
describe("queryMatchesProviderOnly", () => {
|
||||
it("returns true when the query finds the provider but no model id (issue #65374)", () => {
|
||||
// Reproduces the exact case from the issue: typing "aws" locates the
|
||||
// "AWS Build" provider, but none of its Claude model ids contain "aws".
|
||||
const provider = { name: "AWS Build", slug: "aws-build" };
|
||||
const models = ["claude-sonnet-4.5", "claude-sonnet-4", "claude-haiku-4.5"];
|
||||
|
||||
expect(queryMatchesProviderOnly(provider, models, "aws")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when the query also matches a model id — keeps normal filtering", () => {
|
||||
const provider = { name: "AWS Build", slug: "aws-build" };
|
||||
const models = ["claude-sonnet-4.5", "claude-sonnet-4", "claude-haiku-4.5"];
|
||||
|
||||
expect(queryMatchesProviderOnly(provider, models, "sonnet")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when the query does not match the provider at all", () => {
|
||||
const provider = { name: "AWS Build", slug: "aws-build" };
|
||||
const models = ["claude-sonnet-4.5"];
|
||||
|
||||
expect(queryMatchesProviderOnly(provider, models, "openrouter")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for an empty query", () => {
|
||||
const provider = { name: "AWS Build", slug: "aws-build" };
|
||||
const models = ["claude-sonnet-4.5"];
|
||||
|
||||
expect(queryMatchesProviderOnly(provider, models, "")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when there is no selected provider", () => {
|
||||
expect(queryMatchesProviderOnly(null, ["claude-sonnet-4.5"], "aws")).toBe(false);
|
||||
});
|
||||
});
|
||||
23
web/src/lib/model-picker-filter.ts
Normal file
23
web/src/lib/model-picker-filter.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { fuzzyScoreMulti } from "@/lib/fuzzy";
|
||||
|
||||
/**
|
||||
* True when `trimmedQuery` located the selected provider by name/slug but
|
||||
* matches none of its models by id — the case where a single search box
|
||||
* filtering both the provider and model columns would otherwise leave the
|
||||
* model pane empty even though the user just successfully found the
|
||||
* provider they were looking for.
|
||||
*/
|
||||
export function queryMatchesProviderOnly(
|
||||
selectedProvider: { name: string; slug: string } | null,
|
||||
models: readonly string[],
|
||||
trimmedQuery: string,
|
||||
): boolean {
|
||||
if (!trimmedQuery || !selectedProvider) return false;
|
||||
|
||||
const matchesProvider =
|
||||
fuzzyScoreMulti(`${selectedProvider.name} ${selectedProvider.slug}`, trimmedQuery) !=
|
||||
null;
|
||||
const matchesAnyModel = models.some((m) => fuzzyScoreMulti(m, trimmedQuery) != null);
|
||||
|
||||
return matchesProvider && !matchesAnyModel;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue