import type { AgentDefinition } from '@services/agentDefinition/interface'; import defaultAgents from '@services/agentInstance/buildInAgentHandlers/defaultAgents.json'; import { container } from '@services/container'; import type { IDatabaseService } from '@services/database/interface'; import { AgentDefinitionEntity } from '@services/database/schema/agent'; import type { AIGlobalSettings, AIStreamResponse } from '@services/externalAPI/interface'; import type { IPreferenceService } from '@services/preferences/interface'; import serviceIdentifier from '@services/serviceIdentifier'; import { ModelMessage } from 'ai'; import { beforeEach, describe, expect, it, vi } from 'vitest'; describe('ExternalAPIService logging', () => { beforeEach(async () => { vi.clearAllMocks(); // Ensure DatabaseService is initialized with all schemas const databaseService = container.get(serviceIdentifier.Database); await databaseService.initializeForApp(); await container.get(serviceIdentifier.Preference).set('externalAPIDebug', true); // Use the real agent database const dataSource = await databaseService.getDatabase('agent'); const agentDefRepo = dataSource.getRepository(AgentDefinitionEntity); // Clear existing data and add test data await agentDefRepo.clear(); const example = (defaultAgents as unknown as AgentDefinition[])[0]; await agentDefRepo.save({ id: example.id }); }); it('records streaming logs when provider has apiKey (API success)', async () => { const externalAPI = container.get(serviceIdentifier.ExternalAPI); // spy the provider stream to avoid real network and to be deterministic const callProvider = await import('../callProviderAPI'); type StreamReturn = ReturnType; const spy = vi.spyOn(callProvider, 'streamFromProvider').mockImplementation((): StreamReturn => ({ textStream: (async function*() { yield 'hello '; yield 'world'; })(), } as unknown as StreamReturn)); await externalAPI.initialize(); const db = container.get(serviceIdentifier.Database); const aiSettings: AIGlobalSettings = { providers: [{ provider: 'test-provider', apiKey: 'fake', models: [{ name: 'test-model' }] }], defaultConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } }, }; // Mock getSetting to return our test AI settings vi.spyOn(db, 'getSetting').mockImplementation((k: string) => (k === 'aiSettings' ? aiSettings : undefined)); const messages: ModelMessage[] = [{ role: 'user', content: 'hi' }]; const config = await externalAPI.getAIConfig(); const events: AIStreamResponse[] = []; for await (const e of externalAPI.generateFromAI(messages, config, { agentInstanceId: 'agent-instance-1', awaitLogs: true })) events.push(e); const statuses = events.map((e) => e.status); expect(statuses).toContain('start'); expect(statuses).toContain('update'); expect(statuses).toContain('done'); await new Promise((r) => setTimeout(r, 20)); // Check logs from the external API service's database const externalAPILogs = await externalAPI.getAPILogs('agent-instance-1'); expect(externalAPILogs.length).toBeGreaterThan(0); spy.mockRestore(); }); it('records streaming error when apiKey missing (error path)', async () => { const svc = container.get(serviceIdentifier.ExternalAPI); await svc.initialize(); const db = container.get(serviceIdentifier.Database); const aiSettings: AIGlobalSettings = { // Provider without apiKey should trigger an error providers: [{ provider: 'test-provider', models: [{ name: 'test-model' }] }], defaultConfig: { api: { provider: 'test-provider', model: 'test-model' }, modelParameters: { temperature: 0.7, systemPrompt: '', topP: 0.95 } }, }; // Mock getSetting to return our test AI settings vi.spyOn(db, 'getSetting').mockImplementation((k: string) => (k === 'aiSettings' ? aiSettings : undefined)); const messages: ModelMessage[] = [{ role: 'user', content: 'hi' }]; const config = await svc.getAIConfig(); const events: AIStreamResponse[] = []; for await (const e of svc.generateFromAI(messages, config, { agentInstanceId: 'agent-instance-1', awaitLogs: true })) events.push(e); await new Promise((r) => setTimeout(r, 20)); // Check logs from the external API service's database const externalAPILogs = await svc.getAPILogs('agent-instance-1'); expect(externalAPILogs.length).toBeGreaterThan(0); }); });