Skip to content

Commit 1f15d5f

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

5 files changed

Lines changed: 101 additions & 76 deletions

File tree

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

Lines changed: 49 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)
@@ -1043,4 +1038,40 @@ describe("VsCodeLmHandler", () => {
10431038
await expect(promise).rejects.toThrow("VSCode LM completion error: Completion failed")
10441039
})
10451040
})
1041+
1042+
describe("cleanMessageContent / deepClean", () => {
1043+
it("passes through string content unchanged", () => {
1044+
const result = handler["cleanMessageContent"]("hello")
1045+
expect(result).toBe("hello")
1046+
})
1047+
1048+
it("returns falsy values as-is", () => {
1049+
expect(handler["cleanMessageContent"]("")).toBe("")
1050+
})
1051+
1052+
it("recursively cleans array content", () => {
1053+
const input: Anthropic.Messages.MessageParam["content"] = [{ type: "text", text: "hi" }]
1054+
const result = handler["cleanMessageContent"](input)
1055+
expect(result).toEqual([{ type: "text", text: "hi" }])
1056+
})
1057+
1058+
it("recursively cleans nested objects within array items", () => {
1059+
const input: Anthropic.Messages.MessageParam["content"] = [
1060+
{ type: "text", text: "hello" },
1061+
{ type: "text", text: "world" },
1062+
]
1063+
const result = handler["cleanMessageContent"](input)
1064+
expect(result).toEqual(input)
1065+
})
1066+
1067+
it("preserves primitive values other than strings inside objects", () => {
1068+
// deepClean hits the final `return value` branch for non-string primitives
1069+
// Exercise via a nested object whose property value is a number
1070+
const input = [
1071+
{ type: "text", text: "x", extra: 42 },
1072+
] as unknown as Anthropic.Messages.MessageParam["content"]
1073+
const result = handler["cleanMessageContent"](input) as unknown as Array<Record<string, unknown>>
1074+
expect(result[0].extra).toBe(42)
1075+
})
1076+
})
10461077
})

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(

0 commit comments

Comments
 (0)