Skip to content

Commit 7397717

Browse files
committed
format details better
1 parent 92cec65 commit 7397717

File tree

3 files changed

+90
-128
lines changed

3 files changed

+90
-128
lines changed

src/services/huggingface-models.ts

Lines changed: 23 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,29 @@
11
export interface HuggingFaceModel {
2-
_id: string
32
id: string
4-
inferenceProviderMapping: InferenceProviderMapping[]
5-
trendingScore: number
6-
config: ModelConfig
7-
tags: string[]
8-
pipeline_tag: "text-generation" | "image-text-to-text"
9-
library_name?: string
3+
object: "model"
4+
created: number
5+
owned_by: string
6+
providers: Provider[]
107
}
118

12-
export interface InferenceProviderMapping {
9+
export interface Provider {
1310
provider: string
14-
providerId: string
1511
status: "live" | "staging" | "error"
16-
task: "conversational"
17-
}
18-
19-
export interface ModelConfig {
20-
architectures: string[]
21-
model_type: string
22-
tokenizer_config?: {
23-
chat_template?: string | Array<{ name: string; template: string }>
24-
model_max_length?: number
12+
supports_tools?: boolean
13+
supports_structured_output?: boolean
14+
context_length?: number
15+
pricing?: {
16+
input: number
17+
output: number
2518
}
2619
}
2720

28-
interface HuggingFaceApiParams {
29-
pipeline_tag?: "text-generation" | "image-text-to-text"
30-
filter: string
31-
inference_provider: string
32-
limit: number
33-
expand: string[]
34-
}
35-
36-
const DEFAULT_PARAMS: HuggingFaceApiParams = {
37-
filter: "conversational",
38-
inference_provider: "all",
39-
limit: 100,
40-
expand: [
41-
"inferenceProviderMapping",
42-
"config",
43-
"library_name",
44-
"pipeline_tag",
45-
"tags",
46-
"mask_token",
47-
"trendingScore",
48-
],
21+
interface HuggingFaceApiResponse {
22+
object: string
23+
data: HuggingFaceModel[]
4924
}
5025

51-
const BASE_URL = "https://huggingface.co/api/models"
26+
const BASE_URL = "https://router.huggingface.co/v1/models?collection=roocode"
5227
const CACHE_DURATION = 1000 * 60 * 60 // 1 hour
5328

5429
interface CacheEntry {
@@ -59,24 +34,6 @@ interface CacheEntry {
5934

6035
let cache: CacheEntry | null = null
6136

62-
function buildApiUrl(params: HuggingFaceApiParams): string {
63-
const url = new URL(BASE_URL)
64-
65-
// Add simple params
66-
Object.entries(params).forEach(([key, value]) => {
67-
if (!Array.isArray(value)) {
68-
url.searchParams.append(key, String(value))
69-
}
70-
})
71-
72-
// Handle array params specially
73-
params.expand.forEach((item) => {
74-
url.searchParams.append("expand[]", item)
75-
})
76-
77-
return url.toString()
78-
}
79-
8037
const headers: HeadersInit = {
8138
"Upgrade-Insecure-Requests": "1",
8239
"Sec-Fetch-Dest": "document",
@@ -107,45 +64,25 @@ export async function fetchHuggingFaceModels(): Promise<HuggingFaceModel[]> {
10764
try {
10865
console.log("Fetching Hugging Face models from API...")
10966

110-
// Fetch both text-generation and image-text-to-text models in parallel
111-
const [textGenResponse, imgTextResponse] = await Promise.allSettled([
112-
fetch(buildApiUrl({ ...DEFAULT_PARAMS, pipeline_tag: "text-generation" }), requestInit),
113-
fetch(buildApiUrl({ ...DEFAULT_PARAMS, pipeline_tag: "image-text-to-text" }), requestInit),
114-
])
115-
116-
let textGenModels: HuggingFaceModel[] = []
117-
let imgTextModels: HuggingFaceModel[] = []
118-
let hasErrors = false
119-
120-
// Process text-generation models
121-
if (textGenResponse.status === "fulfilled" && textGenResponse.value.ok) {
122-
textGenModels = await textGenResponse.value.json()
123-
} else {
124-
console.error("Failed to fetch text-generation models:", textGenResponse)
125-
hasErrors = true
126-
}
67+
const response = await fetch(BASE_URL, requestInit)
12768

128-
// Process image-text-to-text models
129-
if (imgTextResponse.status === "fulfilled" && imgTextResponse.value.ok) {
130-
imgTextModels = await imgTextResponse.value.json()
131-
} else {
132-
console.error("Failed to fetch image-text-to-text models:", imgTextResponse)
133-
hasErrors = true
69+
if (!response.ok) {
70+
throw new Error(`HTTP error! status: ${response.status}`)
13471
}
13572

136-
// Combine and filter models
137-
const allModels = [...textGenModels, ...imgTextModels]
138-
.filter((model) => model.inferenceProviderMapping.length > 0)
73+
const apiResponse: HuggingFaceApiResponse = await response.json()
74+
const allModels = apiResponse.data
75+
.filter((model) => model.providers.length > 0)
13976
.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase()))
14077

14178
// Update cache
14279
cache = {
14380
data: allModels,
14481
timestamp: now,
145-
status: hasErrors ? "partial" : "success",
82+
status: "success",
14683
}
14784

148-
console.log(`Fetched ${allModels.length} Hugging Face models (status: ${cache.status})`)
85+
console.log(`Fetched ${allModels.length} Hugging Face models`)
14986
return allModels
15087
} catch (error) {
15188
console.error("Error fetching Hugging Face models:", error)

src/shared/ExtensionMessage.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,21 @@ export interface ExtensionMessage {
138138
lmStudioModels?: string[]
139139
vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
140140
huggingFaceModels?: Array<{
141-
_id: string
142141
id: string
143-
inferenceProviderMapping: Array<{
142+
object: string
143+
created: number
144+
owned_by: string
145+
providers: Array<{
144146
provider: string
145-
providerId: string
146147
status: "live" | "staging" | "error"
147-
task: "conversational"
148-
}>
149-
trendingScore: number
150-
config: {
151-
architectures: string[]
152-
model_type: string
153-
tokenizer_config?: {
154-
chat_template?: string | Array<{ name: string; template: string }>
155-
model_max_length?: number
148+
supports_tools?: boolean
149+
supports_structured_output?: boolean
150+
context_length?: number
151+
pricing?: {
152+
input: number
153+
output: number
156154
}
157-
}
158-
tags: string[]
159-
pipeline_tag: "text-generation" | "image-text-to-text"
160-
library_name?: string
155+
}>
161156
}>
162157
mcpServers?: McpServer[]
163158
commits?: GitCommit[]

webview-ui/src/components/settings/providers/HuggingFace.tsx

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,24 @@ import { cn } from "@src/lib/utils"
1313
import { TemperatureControl } from "../TemperatureControl"
1414

1515
import { inputEventTransform } from "../transforms"
16+
import { formatPrice } from "@/utils/formatPrice"
1617

1718
type HuggingFaceModel = {
18-
_id: string
1919
id: string
20-
inferenceProviderMapping: Array<{
20+
object: string
21+
created: number
22+
owned_by: string
23+
providers: Array<{
2124
provider: string
22-
providerId: string
2325
status: "live" | "staging" | "error"
24-
task: "conversational"
25-
}>
26-
trendingScore: number
27-
config: {
28-
architectures: string[]
29-
model_type: string
30-
tokenizer_config?: {
31-
chat_template?: string | Array<{ name: string; template: string }>
32-
model_max_length?: number
26+
supports_tools?: boolean
27+
supports_structured_output?: boolean
28+
context_length?: number
29+
pricing?: {
30+
input: number
31+
output: number
3332
}
34-
}
35-
tags: string[]
36-
pipeline_tag: "text-generation" | "image-text-to-text"
37-
library_name?: string
33+
}>
3834
}
3935

4036
type HuggingFaceProps = {
@@ -83,10 +79,7 @@ export const HuggingFace = ({ apiConfiguration, setApiConfigurationField }: Hugg
8379

8480
// Get current model and its providers
8581
const currentModel = models.find((m) => m.id === apiConfiguration?.huggingFaceModelId)
86-
const availableProviders = useMemo(
87-
() => currentModel?.inferenceProviderMapping || [],
88-
[currentModel?.inferenceProviderMapping],
89-
)
82+
const availableProviders = useMemo(() => currentModel?.providers || [], [currentModel?.providers])
9083

9184
// Set default provider when model changes
9285
useEffect(() => {
@@ -142,18 +135,31 @@ export const HuggingFace = ({ apiConfiguration, setApiConfigurationField }: Hugg
142135
return nameMap[provider] || provider.charAt(0).toUpperCase() + provider.slice(1)
143136
}
144137

145-
// Get model capabilities
138+
// Get current provider
139+
const currentProvider = useMemo(() => {
140+
if (!currentModel || !selectedProvider || selectedProvider === "auto") return null
141+
return currentModel.providers.find((p) => p.provider === selectedProvider)
142+
}, [currentModel, selectedProvider])
143+
144+
// Get model capabilities based on current provider
146145
const modelCapabilities = useMemo(() => {
147146
if (!currentModel) return null
148147

149-
const supportsImages = currentModel.pipeline_tag === "image-text-to-text"
150-
const maxTokens = currentModel.config.tokenizer_config?.model_max_length
148+
// For now, assume text-only models since we don't have pipeline_tag in new API
149+
// This could be enhanced by checking model name patterns or adding vision support detection
150+
const supportsImages = false
151+
152+
// Use provider-specific capabilities if a specific provider is selected
153+
const maxTokens =
154+
currentProvider?.context_length || currentModel.providers.find((p) => p.context_length)?.context_length
155+
const supportsTools = currentProvider?.supports_tools || currentModel.providers.some((p) => p.supports_tools)
151156

152157
return {
153158
supportsImages,
154159
maxTokens,
160+
supportsTools,
155161
}
156-
}, [currentModel])
162+
}, [currentModel, currentProvider])
157163

158164
// Model capabilities component
159165
const ModelCapabilities = () => {
@@ -174,12 +180,36 @@ export const HuggingFace = ({ apiConfiguration, setApiConfigurationField }: Hugg
174180
/>
175181
{modelCapabilities.supportsImages ? "Supports images" : "Text only"}
176182
</div>
183+
<div
184+
className={cn(
185+
"flex items-center gap-1",
186+
modelCapabilities.supportsTools
187+
? "text-vscode-charts-green"
188+
: "text-vscode-errorForeground",
189+
)}>
190+
<span
191+
className={cn("codicon", modelCapabilities.supportsTools ? "codicon-check" : "codicon-x")}
192+
/>
193+
{modelCapabilities.supportsTools ? "Supports tool calling" : "No tool calling"}
194+
</div>
177195
{modelCapabilities.maxTokens && (
178196
<div className="flex items-center gap-1 text-vscode-descriptionForeground">
179-
<span className="codicon codicon-info" />
180-
Max context: {modelCapabilities.maxTokens.toLocaleString()} tokens
197+
<span className="font-medium">{t("settings:modelInfo.maxOutput")}:</span>{" "}
198+
{modelCapabilities.maxTokens.toLocaleString()} tokens
181199
</div>
182200
)}
201+
{currentProvider?.pricing && (
202+
<>
203+
<div className="flex items-center gap-1 text-vscode-descriptionForeground">
204+
<span className="font-medium">{t("settings:modelInfo.inputPrice")}:</span>{" "}
205+
{formatPrice(currentProvider.pricing.input)} / 1M tokens
206+
</div>
207+
<div className="flex items-center gap-1 text-vscode-descriptionForeground">
208+
<span className="font-medium">{t("settings:modelInfo.outputPrice")}:</span>{" "}
209+
{formatPrice(currentProvider.pricing.output)} / 1M tokens
210+
</div>
211+
</>
212+
)}
183213
</div>
184214
</div>
185215
)

0 commit comments

Comments
 (0)