chore(lint): burn down remaining warnings to zero

This commit is contained in:
William Valentin
2026-02-15 23:14:21 -08:00
parent 49b752e8b0
commit 948d4ac6d8
67 changed files with 235 additions and 256 deletions
+3 -2
View File
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import { AnthropicClient } from './anthropic.js';
// Shared mock function so we can override per-test
@@ -99,7 +99,8 @@ describe('AnthropicClient tool use', () => {
expect(response.stopReason).toBe('tool_use');
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls![0]).toEqual({
const firstToolCall = response.toolCalls?.[0];
expect(firstToolCall).toEqual({
id: 'toolu_01',
name: 'shell.exec',
args: { command: 'ls' },
+3 -2
View File
@@ -83,8 +83,9 @@ describe('BedrockClient', () => {
expect(response.stopReason).toBe('tool_use');
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls![0].name).toBe('shell.exec');
expect(response.toolCalls![0].args).toEqual({ command: 'ls' });
const firstToolCall = response.toolCalls?.[0];
expect(firstToolCall?.name).toBe('shell.exec');
expect(firstToolCall?.args).toEqual({ command: 'ls' });
});
it('uses default region when none provided', async () => {
+3 -2
View File
@@ -247,8 +247,9 @@ describe('GeminiClient tool use', () => {
expect(response.stopReason).toBe('tool_use');
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls![0].name).toBe('shell.exec');
expect(response.toolCalls![0].args).toEqual({ command: 'ls' });
const firstToolCall = response.toolCalls?.[0];
expect(firstToolCall?.name).toBe('shell.exec');
expect(firstToolCall?.args).toEqual({ command: 'ls' });
// Verify tools were passed to getGenerativeModel
expect(mockGetGenerativeModel).toHaveBeenCalledWith(
+1 -1
View File
@@ -1,6 +1,6 @@
import { GoogleGenerativeAI } from '@google/generative-ai';
import type { GenerativeModel, Content, Part, FunctionDeclaration, FunctionDeclarationSchema } from '@google/generative-ai';
import type { ChatRequest, ChatResponse, ChatStreamEvent, ModelClient, ModelToolCall, ToolDefinition, Message, MessageContentPart } from './types.js';
import type { ChatRequest, ChatResponse, ChatStreamEvent, ModelClient, ModelToolCall, ToolDefinition, Message } from './types.js';
export interface GeminiClientConfig {
apiKey?: string;
+9 -3
View File
@@ -31,9 +31,15 @@ function toOpenAIContent(content: string | MessageContentPart[]): string | OpenA
return { type: 'text', text: part.text };
}
if (part.type === 'image') {
if (part.source.type === 'base64' && !part.source.data) {
return { type: 'text', text: '[Image omitted: missing base64 data]' };
}
if (part.source.type !== 'base64' && !part.source.url) {
return { type: 'text', text: '[Image omitted: missing URL]' };
}
const url = part.source.type === 'base64'
? `data:${part.source.media_type};base64,${part.source.data!}`
: part.source.url!;
? `data:${part.source.media_type};base64,${part.source.data}`
: part.source.url;
return { type: 'image_url', image_url: { url } };
}
if (part.type === 'audio') {
@@ -154,7 +160,7 @@ export class GitHubModelsClient implements ModelClient {
// Extended thinking/reasoning mode
if (request.thinking) {
(params as any).reasoning_effort = 'medium';
(params as OpenAI.ChatCompletionCreateParamsNonStreaming & { reasoning_effort?: 'low' | 'medium' | 'high' }).reasoning_effort = 'medium';
}
const response = await this.client.chat.completions.create(params);
+4 -3
View File
@@ -152,7 +152,7 @@ export function normalizeMessagesForLlamaCpp(
} catch {
argsStr = '{}';
}
toolCalls!.push({
toolCalls.push({
id,
type: 'function',
function: {
@@ -167,7 +167,7 @@ export function normalizeMessagesForLlamaCpp(
role: 'assistant',
content: textParts.join('\n'),
};
if (toolCalls!.length > 0) {
if (toolCalls.length > 0) {
chatMsg.tool_calls = toolCalls;
}
result.push(chatMsg);
@@ -381,7 +381,8 @@ export class LlamaCppClient implements ModelClient {
arguments: '',
});
}
const acc = toolCallAccumulators.get(tc.index)!;
const acc = toolCallAccumulators.get(tc.index);
if (!acc) {continue;}
if (tc.function?.name) {acc.name = tc.function.name;}
if (tc.function?.arguments) {acc.arguments += tc.function.arguments;}
}
+6 -3
View File
@@ -57,13 +57,13 @@ const mp3AudioAttachment: Attachment = makeAttachment({
filename: 'audio.mp3',
});
const wavAudioAttachment: Attachment = makeAttachment({
const _wavAudioAttachment: Attachment = makeAttachment({
mimeType: 'audio/wav',
data: 'UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA=', // Base64 of a short WAV
filename: 'audio.wav',
});
const m4aAudioAttachment: Attachment = makeAttachment({
const _m4aAudioAttachment: Attachment = makeAttachment({
mimeType: 'audio/x-m4a',
data: 'AAAAUGV0Zi4xLjAgc291cmNlIGZvciBzdGFydHBvaW50', // Base64 of M4A
filename: 'audio.m4a',
@@ -545,8 +545,11 @@ describe('buildUserMessageWithAudio', () => {
const content = Array.isArray(result.content) ? result.content : [{ type: 'text' as const, text: result.content }];
const textPart = content.find((p) => p.type === 'text') as { type: 'text'; text: string } | undefined;
expect(textPart).toBeDefined();
if (!textPart) {
throw new Error('Expected text content part');
}
const textContent = textPart!.text || '';
const textContent = textPart.text || '';
const firstVoiceIndex = textContent.indexOf('[Voice message]:');
const textIndex = textContent.indexOf(textMessage);
+4 -1
View File
@@ -241,9 +241,12 @@ export async function transcribeAudio(
if (!config?.endpoint) {
return '[Audio message received but no transcription service is configured]';
}
if (!attachment.data) {
return '[Audio message transcription failed]';
}
try {
const audioBuffer = Buffer.from(attachment.data!, 'base64');
const audioBuffer = Buffer.from(attachment.data, 'base64');
const ext = mimeToExtension(attachment.mimeType);
const formData = new FormData();
formData.append('file', new Blob([audioBuffer], { type: attachment.mimeType }), `audio.${ext}`);
+2 -2
View File
@@ -1,10 +1,10 @@
import { describe, expect, it, vi } from 'vitest';
let capturedOptions: any;
let capturedOptions: Record<string, unknown> | undefined;
vi.mock('openai', () => {
class OpenAI {
constructor(options: any) {
constructor(options: Record<string, unknown>) {
capturedOptions = options;
}
}
+2 -1
View File
@@ -83,7 +83,8 @@ describe('OpenAIClient tool use', () => {
expect(response.stopReason).toBe('tool_use');
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls![0]).toEqual({
const firstToolCall = response.toolCalls?.[0];
expect(firstToolCall).toEqual({
id: 'call_1',
name: 'shell.exec',
args: { command: 'ls' },
+8 -3
View File
@@ -39,7 +39,10 @@ describe('SyntheticClient', () => {
it('streams content + done', async () => {
const client = new SyntheticClient({ model: 'fixed:stream-me' });
const events: string[] = [];
for await (const ev of client.chatStream!(makeRequest('x'))) {
if (!client.chatStream) {
throw new Error('Expected streaming support');
}
for await (const ev of client.chatStream(makeRequest('x'))) {
events.push(ev.type);
}
expect(events).toEqual(['content', 'done']);
@@ -49,7 +52,10 @@ describe('SyntheticClient', () => {
const client = new SyntheticClient({ model: 'echo' });
const types: string[] = [];
const toolNames: string[] = [];
for await (const ev of client.chatStream!(makeRequest('@tool file.read {"path":"README.md"}'))) {
if (!client.chatStream) {
throw new Error('Expected streaming support');
}
for await (const ev of client.chatStream(makeRequest('@tool file.read {"path":"README.md"}'))) {
types.push(ev.type);
if (ev.type === 'tool_use' && ev.toolCall) {
toolNames.push(ev.toolCall.name);
@@ -59,4 +65,3 @@ describe('SyntheticClient', () => {
expect(toolNames).toEqual(['file.read']);
});
});