chore(lint): burn down remaining warnings to zero
This commit is contained in:
@@ -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' },
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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}`);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user