Skip to content

Commit 99dc743

Browse files
committed
fix(types): eliminate any casts in vscode-lm provider and format transform
1 parent b8d31f4 commit 99dc743

5 files changed

Lines changed: 65 additions & 76 deletions

File tree

‎src/api/providers/__tests__/vscode-lm.spec.ts‎

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@ vi.mock("vscode", () => {
1212
constructor(
1313
public callId: string,
1414
public name: string,
15-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16-
public input: any,
15+
public input: object,
1716
) {}
1817
}
1918

2019
return {
2120
workspace: {
2221
getConfiguration: vi.fn(() => ({
23-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
24-
get: vi.fn((key: string, defaultValue: any) => defaultValue),
22+
get: vi.fn((key: string, defaultValue: unknown) => defaultValue),
2523
})),
2624
onDidChangeConfiguration: vi.fn((_callback) => ({
2725
dispose: vi.fn(),
@@ -503,8 +501,7 @@ describe("VsCodeLmHandler", () => {
503501
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
504502
stream: (async function* () {
505503
// Yield an unknown chunk type (not TextPart, not ToolCallPart)
506-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
507-
yield { type: "unknown", foo: "bar" } as any
504+
yield { type: "unknown", foo: "bar" } as unknown as vscode.LanguageModelTextPart
508505
return
509506
})(),
510507
text: (async function* () {
@@ -533,8 +530,7 @@ describe("VsCodeLmHandler", () => {
533530
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
534531

535532
// Create a TextPart with a non-string value (number)
536-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
537-
const badTextPart = new vscode.LanguageModelTextPart(42 as any)
533+
const badTextPart = new vscode.LanguageModelTextPart(42 as unknown as string)
538534
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
539535
stream: (async function* () {
540536
yield badTextPart
@@ -566,8 +562,7 @@ describe("VsCodeLmHandler", () => {
566562
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
567563

568564
// Create a ToolCallPart with a non-string callId
569-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
570-
const badToolCall = new vscode.LanguageModelToolCallPart(123 as any, "valid-name", {})
565+
const badToolCall = new vscode.LanguageModelToolCallPart(123 as unknown as string, "valid-name", {})
571566
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
572567
stream: (async function* () {
573568
yield badToolCall
@@ -599,8 +594,11 @@ describe("VsCodeLmHandler", () => {
599594
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
600595

601596
// Create a ToolCallPart with a string input (not an object)
602-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
603-
const badToolCall = new vscode.LanguageModelToolCallPart("call-1", "valid-name", "not-an-object" as any)
597+
const badToolCall = new vscode.LanguageModelToolCallPart(
598+
"call-1",
599+
"valid-name",
600+
"not-an-object" as unknown as object,
601+
)
604602
mockLanguageModelChat.sendRequest.mockResolvedValueOnce({
605603
stream: (async function* () {
606604
yield badToolCall
@@ -632,8 +630,7 @@ describe("VsCodeLmHandler", () => {
632630
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
633631

634632
// Create a ToolCallPart with circular input that will throw on JSON.stringify
635-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
636-
const circularInput: any = { name: "circular" }
633+
const circularInput: Record<string, unknown> = { name: "circular" }
637634
circularInput.self = circularInput
638635

639636
const badToolCall = new vscode.LanguageModelToolCallPart("call-1", "valid-name", circularInput)
@@ -909,8 +906,7 @@ describe("VsCodeLmHandler", () => {
909906
cancel: vi.fn(),
910907
dispose: vi.fn(),
911908
}
912-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
913-
handler["currentRequestCancellation"] = mockCancellation as any
909+
handler["currentRequestCancellation"] = mockCancellation as unknown as vscode.CancellationTokenSource
914910

915911
mockLanguageModelChat.countTokens.mockResolvedValueOnce(50)
916912

@@ -970,8 +966,7 @@ describe("VsCodeLmHandler", () => {
970966
handler["currentRequestCancellation"] = null
971967
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
972968

973-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
974-
mockLanguageModelChat.countTokens.mockResolvedValueOnce("not-a-number" as any)
969+
mockLanguageModelChat.countTokens.mockResolvedValueOnce("not-a-number" as unknown as number)
975970

976971
const content: Anthropic.Messages.ContentBlockParam[] = [{ type: "text", text: "test" }]
977972
const result = await handler.countTokens(content)

‎src/api/providers/vscode-lm.ts‎

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -338,28 +338,34 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan
338338
return this.client
339339
}
340340

341-
private cleanMessageContent(content: any): any {
342-
if (!content) {
343-
return content
341+
private cleanMessageContent(
342+
content: Anthropic.Messages.MessageParam["content"],
343+
): Anthropic.Messages.MessageParam["content"] {
344+
return this.deepClean(content) as Anthropic.Messages.MessageParam["content"]
345+
}
346+
347+
private deepClean(value: unknown): unknown {
348+
if (!value) {
349+
return value
344350
}
345351

346-
if (typeof content === "string") {
347-
return content
352+
if (typeof value === "string") {
353+
return value
348354
}
349355

350-
if (Array.isArray(content)) {
351-
return content.map((item) => this.cleanMessageContent(item))
356+
if (Array.isArray(value)) {
357+
return value.map((item) => this.deepClean(item))
352358
}
353359

354-
if (typeof content === "object") {
355-
const cleaned: any = {}
356-
for (const [key, value] of Object.entries(content)) {
357-
cleaned[key] = this.cleanMessageContent(value)
360+
if (typeof value === "object") {
361+
const cleaned: Record<string, unknown> = {}
362+
for (const [key, v] of Object.entries(value)) {
363+
cleaned[key] = this.deepClean(v)
358364
}
359365
return cleaned
360366
}
361367

362-
return content
368+
return value
363369
}
364370

365371
override async *createMessage(

‎src/api/transform/__tests__/vscode-lm-format.spec.ts‎

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ interface MockLanguageModelTextPart {
1616
value: string
1717
}
1818

19+
type MockLanguageModelChatMessage = {
20+
role: string
21+
content: unknown
22+
}
23+
1924
interface MockLanguageModelToolCallPart {
2025
type: "tool_call"
2126
callId: string
2227
name: string
23-
input: any
28+
input: object
2429
}
2530

2631
interface MockLanguageModelToolResultPart {
@@ -46,7 +51,7 @@ vitest.mock("vscode", () => {
4651
constructor(
4752
public callId: string,
4853
public name: string,
49-
public input: any,
54+
public input: object,
5055
) {}
5156
}
5257

@@ -165,7 +170,7 @@ describe("convertToVsCodeLmMessages", () => {
165170
type: "tool_use",
166171
id: "tool-num",
167172
name: "numericTool",
168-
input: 42 as any, // number is valid JSON
173+
input: 42 as unknown as object, // number is valid JSON
169174
},
170175
],
171176
},
@@ -241,7 +246,7 @@ describe("convertToVsCodeLmMessages", () => {
241246
content: [
242247
{
243248
type: "image",
244-
source: { type: "url", url: "https://example.com/img.png" } as any,
249+
source: { type: "url", url: "https://example.com/img.png" },
245250
},
246251
],
247252
},
@@ -263,7 +268,7 @@ describe("convertToVsCodeLmMessages", () => {
263268
content: [
264269
{
265270
type: "image",
266-
source: { type: "url", url: "https://example.com/img.png" } as any,
271+
source: { type: "url", url: "https://example.com/img.png" },
267272
},
268273
],
269274
},
@@ -272,7 +277,7 @@ describe("convertToVsCodeLmMessages", () => {
272277
]
273278

274279
const result = convertToVsCodeLmMessages(messages)
275-
const toolResult = result[0].content[0] as any
280+
const toolResult = result[0].content[0] as MockLanguageModelToolResultPart
276281
expect(toolResult.content[0].value).toContain("[Image (url): not supported by VSCode LM API]")
277282
})
278283

@@ -296,7 +301,7 @@ describe("convertToVsCodeLmMessages", () => {
296301
]
297302

298303
const result = convertToVsCodeLmMessages(messages)
299-
const toolResult = result[0].content[0] as any
304+
const toolResult = result[0].content[0] as MockLanguageModelToolResultPart
300305
expect(toolResult.content[0].value).toBe("[Image (base64): image/jpeg not supported by VSCode LM API]")
301306
})
302307

@@ -308,31 +313,31 @@ describe("convertToVsCodeLmMessages", () => {
308313
{
309314
type: "tool_result",
310315
tool_use_id: "tool-1",
311-
content: [{ type: "document" } as any],
316+
content: [{ type: "document" } as unknown as Anthropic.Messages.DocumentBlockParam],
312317
},
313318
],
314319
},
315320
]
316321

317322
const result = convertToVsCodeLmMessages(messages)
318-
const toolResult = result[0].content[0] as any
323+
const toolResult = result[0].content[0] as MockLanguageModelToolResultPart
319324
expect(toolResult.content[0].value).toBe("")
320325
})
321326
})
322327

323328
describe("convertToAnthropicRole", () => {
324329
it("should convert assistant role correctly", () => {
325-
const result = convertToAnthropicRole("assistant" as any)
330+
const result = convertToAnthropicRole(vscode.LanguageModelChatMessageRole.Assistant)
326331
expect(result).toBe("assistant")
327332
})
328333

329334
it("should convert user role correctly", () => {
330-
const result = convertToAnthropicRole("user" as any)
335+
const result = convertToAnthropicRole(vscode.LanguageModelChatMessageRole.User)
331336
expect(result).toBe("user")
332337
})
333338

334339
it("should return null for unknown roles", () => {
335-
const result = convertToAnthropicRole("unknown" as any)
340+
const result = convertToAnthropicRole("unknown" as unknown as vscode.LanguageModelChatMessageRole)
336341
expect(result).toBeNull()
337342
})
338343
})
@@ -342,7 +347,7 @@ describe("extractTextCountFromMessage", () => {
342347
const message = {
343348
role: "user",
344349
content: "Hello world",
345-
} as any
350+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
346351

347352
const result = extractTextCountFromMessage(message)
348353
expect(result).toBe("Hello world")
@@ -353,7 +358,7 @@ describe("extractTextCountFromMessage", () => {
353358
const message = {
354359
role: "user",
355360
content: [mockTextPart],
356-
} as any
361+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
357362

358363
const result = extractTextCountFromMessage(message)
359364
expect(result).toBe("Text content")
@@ -365,7 +370,7 @@ describe("extractTextCountFromMessage", () => {
365370
const message = {
366371
role: "user",
367372
content: [mockTextPart1, mockTextPart2],
368-
} as any
373+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
369374

370375
const result = extractTextCountFromMessage(message)
371376
expect(result).toBe("First partSecond part")
@@ -379,7 +384,7 @@ describe("extractTextCountFromMessage", () => {
379384
const message = {
380385
role: "user",
381386
content: [mockToolResultPart],
382-
} as any
387+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
383388

384389
const result = extractTextCountFromMessage(message)
385390
expect(result).toBe("tool-result-idTool result content")
@@ -390,7 +395,7 @@ describe("extractTextCountFromMessage", () => {
390395
const message = {
391396
role: "assistant",
392397
content: [mockToolCallPart],
393-
} as any
398+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
394399

395400
const result = extractTextCountFromMessage(message)
396401
expect(result).toBe("tool-namecall-id")
@@ -406,7 +411,7 @@ describe("extractTextCountFromMessage", () => {
406411
const message = {
407412
role: "assistant",
408413
content: [mockToolCallPart],
409-
} as any
414+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
410415

411416
const result = extractTextCountFromMessage(message)
412417
expect(result).toBe(`calculatorcall-id${JSON.stringify(mockInput)}`)
@@ -417,7 +422,7 @@ describe("extractTextCountFromMessage", () => {
417422
const message = {
418423
role: "assistant",
419424
content: [mockToolCallPart],
420-
} as any
425+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
421426

422427
const result = extractTextCountFromMessage(message)
423428
expect(result).toBe("tool-namecall-id")
@@ -435,7 +440,7 @@ describe("extractTextCountFromMessage", () => {
435440
const message = {
436441
role: "assistant",
437442
content: [mockTextPart, mockToolResultPart, mockToolCallPart],
438-
} as any
443+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
439444

440445
const result = extractTextCountFromMessage(message)
441446
expect(result).toBe(`Text contentresult-idTool resulttoolcall-id${JSON.stringify(mockInput)}`)
@@ -445,7 +450,7 @@ describe("extractTextCountFromMessage", () => {
445450
const message = {
446451
role: "user",
447452
content: [],
448-
} as any
453+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
449454

450455
const result = extractTextCountFromMessage(message)
451456
expect(result).toBe("")
@@ -455,7 +460,7 @@ describe("extractTextCountFromMessage", () => {
455460
const message = {
456461
role: "user",
457462
content: undefined,
458-
} as any
463+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
459464

460465
const result = extractTextCountFromMessage(message)
461466
expect(result).toBe("")
@@ -472,7 +477,7 @@ describe("extractTextCountFromMessage", () => {
472477
const message = {
473478
role: "user",
474479
content: [mockToolResultPart],
475-
} as any
480+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
476481

477482
const result = extractTextCountFromMessage(message)
478483
expect(result).toBe("result-idPart 1Part 2")
@@ -484,7 +489,7 @@ describe("extractTextCountFromMessage", () => {
484489
const message = {
485490
role: "user",
486491
content: [mockToolResultPart],
487-
} as any
492+
} satisfies MockLanguageModelChatMessage as unknown as vscode.LanguageModelChatMessage
488493

489494
const result = extractTextCountFromMessage(message)
490495
expect(result).toBe("result-id")
@@ -494,8 +499,7 @@ describe("extractTextCountFromMessage", () => {
494499
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
495500

496501
// Create an object with a circular reference that will throw on JSON.stringify
497-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
498-
const circularInput: any = { name: "circular" }
502+
const circularInput: Record<string, unknown> = { name: "circular" }
499503
circularInput.self = circularInput
500504

501505
const mockToolCallPart = new (vitest.mocked(vscode).LanguageModelToolCallPart)(
@@ -504,13 +508,12 @@ describe("extractTextCountFromMessage", () => {
504508
circularInput,
505509
)
506510

507-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
508-
const message: any = {
509-
role: vscode.LanguageModelChatMessageRole.Assistant,
511+
const message: MockLanguageModelChatMessage = {
512+
role: "assistant",
510513
content: [mockToolCallPart],
511514
}
512515

513-
const result = extractTextCountFromMessage(message)
516+
const result = extractTextCountFromMessage(message as unknown as vscode.LanguageModelChatMessage)
514517

515518
// Should still return the tool name and callId even when input stringify fails
516519
expect(result).toBe("broken-toolcall-id")

‎src/api/transform/vscode-lm-format.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as vscode from "vscode"
44
/**
55
* Safely converts a value into a plain object.
66
*/
7-
function asObjectSafe(value: any): object {
7+
function asObjectSafe(value: unknown): object {
88
// Handle null/undefined
99
if (!value) {
1010
return {}

0 commit comments

Comments
 (0)