Skip to content

Commit f028ba3

Browse files
fix: simplify model discovery follow-ups
1 parent 85fc830 commit f028ba3

File tree

16 files changed

+42
-216
lines changed

16 files changed

+42
-216
lines changed

packages/backend/src/routing/model-discovery/free-tier-models.spec.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

packages/backend/src/routing/model-discovery/free-tier-models.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

packages/backend/src/routing/model-discovery/provider-model-fetcher.service.spec.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,12 @@ describe('ProviderModelFetcherService', () => {
310310
fetchSpy.mockResolvedValue({
311311
ok: true,
312312
json: async () => ({
313-
result: [{ id: '@cf/meta/llama-3.3-70b-instruct-fp8-fast', task: 'text-generation' }],
313+
result: [
314+
{
315+
id: '@cf/meta/llama-3.3-70b-instruct-fp8-fast',
316+
task: { name: 'Text Generation' },
317+
},
318+
],
314319
}),
315320
});
316321

@@ -331,10 +336,10 @@ describe('ProviderModelFetcherService', () => {
331336
ok: true,
332337
json: async () => ({
333338
result: [
334-
{ name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast', task: 'Text Generation' },
335-
{ name: '@cf/openai/whisper', task: 'Automatic Speech Recognition' },
336-
{ name: '@cf/baai/bge-m3', task: 'Text Embeddings' },
337-
{ name: '@cf/black-forest-labs/flux-1', task: 'Text-to-Image' },
339+
{ name: '@cf/meta/llama-3.3-70b-instruct-fp8-fast', task: { name: 'Text Generation' } },
340+
{ name: '@cf/openai/whisper', task: { name: 'Automatic Speech Recognition' } },
341+
{ name: '@cf/baai/bge-m3', task: { name: 'Text Embeddings' } },
342+
{ name: '@cf/black-forest-labs/flux-1', task: { name: 'Text-to-Image' } },
338343
{ name: '@cf/no-task-model' },
339344
],
340345
}),
@@ -357,7 +362,7 @@ describe('ProviderModelFetcherService', () => {
357362
{
358363
id: 'f9f2250b-1048-4a52-9910-d0bf976616a1',
359364
name: '@cf/openai/gpt-oss-120b',
360-
task: 'text-generation',
365+
task: { name: 'Text Generation' },
361366
},
362367
],
363368
}),

packages/backend/src/routing/model-discovery/provider-model-fetcher.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,12 @@ function parseHuggingFace(body: unknown, provider: string): DiscoveredModel[] {
300300

301301
function extractCloudflareTaskNames(record: UnknownRecord | null): string[] {
302302
if (!record) return [];
303-
const tasks = [
304-
...readStringList(record, 'tasks'),
305-
...readStringList(asRecord(record['task']), 'labels'),
306-
];
303+
const taskRecord = asRecord(record['task']);
304+
const tasks = [...readStringList(record, 'tasks'), ...readStringList(taskRecord, 'labels')];
307305
const directTask = readString(record, 'task', 'task_name', 'source_task');
308306
if (directTask) tasks.push(directTask);
307+
const taskName = readString(taskRecord, 'name');
308+
if (taskName) tasks.push(taskName);
309309
return tasks.map((task) => task.toLowerCase());
310310
}
311311

packages/backend/src/routing/routing.controller.spec.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,6 @@ describe('RoutingController', () => {
531531
capability_reasoning: false,
532532
capability_code: true,
533533
quality_score: 3,
534-
is_free: false,
535534
display_name: 'GPT-4o',
536535
});
537536
});
@@ -550,25 +549,13 @@ describe('RoutingController', () => {
550549
'context_window',
551550
'display_name',
552551
'input_price_per_token',
553-
'is_free',
554552
'model_name',
555553
'output_price_per_token',
556554
'provider',
557555
'quality_score',
558556
]);
559557
});
560558

561-
it('should expose is_free for free-tier models', async () => {
562-
mockDiscoveryService.getModelsForAgent.mockResolvedValue([
563-
makeDiscovered({ id: 'llama-3.1-8b-instant', provider: 'groq' }),
564-
]);
565-
566-
const result = await controller.getAvailableModels(mockUser, mockAgentName);
567-
568-
expect(result).toHaveLength(1);
569-
expect(result[0].is_free).toBe(true);
570-
});
571-
572559
it('should use null for display_name when displayName is empty', async () => {
573560
mockDiscoveryService.getModelsForAgent.mockResolvedValue([
574561
makeDiscovered({ id: 'some-model', provider: 'openai', displayName: '' }),

packages/backend/src/routing/routing.controller.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { RoutingService } from './routing.service';
55
import { ResolveAgentService } from './resolve-agent.service';
66
import { CustomProviderService } from './custom-provider.service';
77
import { ModelDiscoveryService } from './model-discovery/model-discovery.service';
8-
import { isFreeTierModel } from './model-discovery/free-tier-models';
98
import { OllamaSyncService } from '../database/ollama-sync.service';
109
import { CopilotDeviceAuthService } from './copilot-device-auth.service';
1110
import {
@@ -264,13 +263,6 @@ export class RoutingController {
264263
capability_reasoning: m.capabilityReasoning,
265264
capability_code: m.capabilityCode,
266265
quality_score: m.qualityScore,
267-
is_free: isFreeTierModel({
268-
provider: m.provider,
269-
id: m.id,
270-
inputPricePerToken: m.inputPricePerToken,
271-
outputPricePerToken: m.outputPricePerToken,
272-
authType: m.authType ?? 'api_key',
273-
}),
274266
display_name: isCustom ? CustomProviderService.rawModelName(m.id) : m.displayName || null,
275267
...(isCustom && {
276268
provider_display_name: cpNameMap.get(m.provider) ?? m.provider,

packages/frontend/src/components/ModelPickerModal.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,13 @@ function labelForModel(name: string, labels: Map<string, string>): string {
3636
}
3737

3838
const isFreeModel = (m: AvailableModel): boolean =>
39-
m.is_free === true ||
40-
(m.input_price_per_token != null &&
41-
m.output_price_per_token != null &&
42-
Number(m.input_price_per_token) === 0 &&
43-
Number(m.output_price_per_token) === 0);
39+
m.input_price_per_token != null &&
40+
m.output_price_per_token != null &&
41+
Number(m.input_price_per_token) === 0 &&
42+
Number(m.output_price_per_token) === 0;
4443

45-
const priceLabel = (m: AvailableModel): string => {
46-
if (isFreeModel(m) && (m.input_price_per_token == null || m.output_price_per_token == null)) {
47-
return 'Free tier';
48-
}
49-
return `${pricePerM(m.input_price_per_token)} in · ${pricePerM(m.output_price_per_token)} out per 1M`;
50-
};
44+
const priceLabel = (m: AvailableModel): string =>
45+
`${pricePerM(m.input_price_per_token)} in · ${pricePerM(m.output_price_per_token)} out per 1M`;
5146

5247
const ModelPickerModal: Component<Props> = (props) => {
5348
const hasSubscription = () =>

packages/frontend/src/services/api.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,6 @@ export interface AvailableModel {
517517
capability_reasoning: boolean;
518518
capability_code: boolean;
519519
quality_score: number;
520-
is_free?: boolean;
521520
display_name?: string;
522521
provider_display_name?: string;
523522
}

packages/frontend/tests/components/ModelPickerModal.test.tsx

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -166,25 +166,6 @@ describe("ModelPickerModal", () => {
166166
expect(screen.getByText("Free models only")).toBeDefined();
167167
});
168168

169-
it("treats explicit is_free models as free even when pricing is null", () => {
170-
const models = [
171-
{ model_name: "paid-model", provider: "OpenAI", input_price_per_token: 0.0000025, output_price_per_token: 0.00001, context_window: 128000, capability_reasoning: false, capability_code: true, is_free: false },
172-
{ model_name: "free-tier-model", provider: "OpenAI", input_price_per_token: null, output_price_per_token: null, context_window: 128000, capability_reasoning: false, capability_code: true, is_free: true },
173-
{ model_name: "unknown-model", provider: "OpenAI", input_price_per_token: null, output_price_per_token: null, context_window: 128000, capability_reasoning: false, capability_code: true },
174-
];
175-
176-
render(() => (
177-
<ModelPickerModal tierId="simple" models={models} tiers={baseTiers} onSelect={onSelect} onClose={onClose} />
178-
));
179-
180-
fireEvent.click(screen.getByText("Free models only"));
181-
182-
expect(screen.getByText("free-tier-model")).toBeDefined();
183-
expect(screen.getByText("Free tier")).toBeDefined();
184-
expect(screen.queryByText("paid-model")).toBeNull();
185-
expect(screen.queryByText("unknown-model")).toBeNull();
186-
});
187-
188169
it("sorts openrouter/free to the top of its group", () => {
189170
const { container } = render(() => (
190171
<ModelPickerModal tierId="simple" models={baseModels} tiers={baseTiers} onSelect={onSelect} onClose={onClose} />

packages/frontend/tests/pages/Routing.test.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,6 @@ describe("ModelPickerModal — custom providers and filtering", () => {
769769
const models: AvailableModel[] = [
770770
{ model_name: "gpt-4o-mini", provider: "OpenAI", input_price_per_token: 0.00000015, output_price_per_token: 0.0000006, context_window: 128000, capability_reasoning: false, capability_code: true },
771771
{ model_name: "free-model", provider: "OpenAI", input_price_per_token: 0, output_price_per_token: 0, context_window: 128000, capability_reasoning: false, capability_code: false },
772-
{ model_name: "free-tier-model", provider: "Groq", input_price_per_token: null, output_price_per_token: null, context_window: 128000, capability_reasoning: false, capability_code: false, is_free: true },
773772
{ model_name: "null-price-model", provider: "OpenAI", input_price_per_token: null, output_price_per_token: null, context_window: 128000, capability_reasoning: false, capability_code: false },
774773
];
775774

@@ -788,9 +787,8 @@ describe("ModelPickerModal — custom providers and filtering", () => {
788787
const pill = container.querySelector('.routing-modal__filter-pill') as HTMLButtonElement;
789788
fireEvent.click(pill);
790789

791-
// free-model (price=0) and free-tier-model (explicit is_free) should show.
790+
// Only explicit zero-priced models should survive the filter.
792791
expect(screen.getByText("free-model")).toBeDefined();
793-
expect(screen.getByText("free-tier-model")).toBeDefined();
794792
expect(screen.queryByText("null-price-model")).toBeNull();
795793
});
796794

0 commit comments

Comments
 (0)