mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(booking-import): say why a recognised booking could not be imported
A document the model read correctly could still come back as "No reservations could be extracted". Every type mapper can answer null when the node carries no reservationFor, or one with no name, and both call sites pushed the item only if there was one — with no else. The unknown-@type branch next to them does warn, so a node TREK did not recognise was reported while a node it recognised but could not map vanished: no item, no warning, nothing in the log. Because the file did produce a node, the "no reservations found" fallback was skipped too, and the preview arrived empty and silent. Every recognised type could disappear this way, on both the kitinerary and the AI path. Each of them now warns, naming the file, the index, the type and the reason. Three smaller silences went with it. The debug line labelled "Raw LLM Response" printed the already-parsed array, so its inspect formatting — single quotes, bare keys — read as a provider emitting broken JSON and sent this report at the JSON parser, which has handled that shape since 4.0.0. A single reservation answered on its own, with no array and no wrapper, was dropped by the list unwrapper although the schema downstream would have taken it. And a response nobody could read returned an empty list, which is the same answer as a voucher with no booking in it; it now raises, on the Anthropic client too, where a reply cut off at the token limit produced exactly this report's symptom. The plugin AI surface keeps its old empty answer, since a value is what a plugin contracts for. The retry ladder gained a rung that sends no response_format at all, for providers that support neither json_schema nor json_object. Closes #2375
This commit is contained in:
@@ -61,6 +61,7 @@ import { DbRpc } from '../../../src/nest/plugins/host/rpc/db.rpc';
|
||||
import type { PluginUserSettingsService } from '../../../src/nest/plugins/plugin-user-settings.service';
|
||||
import { MetaRpc } from '../../../src/nest/plugins/host/rpc/meta.rpc';
|
||||
import { HostSurfaceRpc } from '../../../src/nest/plugins/host/rpc/host-surface.rpc';
|
||||
import { UnreadableLlmResponse } from '../../../src/nest/llm-parse/clients/openai-compatible.client';
|
||||
import { getPluginDataDb, closePluginDataDb } from '../../../src/nest/plugins/host/plugin-host-state';
|
||||
import { db as mockDb } from '../../../src/db/database';
|
||||
import { DatabaseService } from '../../../src/nest/database/database.service';
|
||||
@@ -472,6 +473,27 @@ describe('HostSurfaceRpc — users, broadcasts, notify, ai, oauth, scheduler', (
|
||||
expect((await call(makeHost('surface', 'ai:invoke'), 'ai.complete', { prompt: 'p' }, 5)).result).toEqual({ text: '' });
|
||||
});
|
||||
|
||||
/*
|
||||
* The clients raise on a response nothing could read so the booking import can
|
||||
* warn per file (#2375). This surface answered an empty value for a model that
|
||||
* talks prose long before that, and it is versioned on its own, so the plugin
|
||||
* keeps the empty value — while a request that genuinely failed still has to
|
||||
* reach it as an error.
|
||||
*/
|
||||
it('HOSTRPC-026a an unreadable answer stays empty here, a failed request does not (#2375)', async () => {
|
||||
const host = makeHost('surface', 'ai:invoke');
|
||||
llmExtract.mockRejectedValueOnce(new UnreadableLlmResponse('the model did not answer with JSON: Sure, happy to help.'));
|
||||
expect((await call(host, 'ai.complete', { prompt: 'p' }, 5)).result).toEqual({ text: '' });
|
||||
|
||||
llmExtract.mockRejectedValueOnce(new UnreadableLlmResponse('the model returned an empty response'));
|
||||
expect((await call(host, 'ai.extract', { text: 't', jsonSchema: { type: 'object' } }, 5)).result).toEqual({ results: [] });
|
||||
|
||||
llmExtract.mockRejectedValueOnce(new Error('LLM request failed (503): upstream is down'));
|
||||
const failed = await call(host, 'ai.complete', { prompt: 'p' }, 5);
|
||||
expect(failed.error?.code).toBe('HOST_ERROR');
|
||||
expect(failed.error?.message).toMatch(/503/);
|
||||
});
|
||||
|
||||
it('HOSTRPC-027 oauth.getToken returns only the acting user\'s access token', async () => {
|
||||
const host = makeHost('surface', 'oauth:client');
|
||||
expect((await call(host, 'oauth.getToken', {}, 5)).result).toEqual({ accessToken: 'tok-5' });
|
||||
|
||||
Reference in New Issue
Block a user