Launch
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
const { normalizeImageUri, toWikimediaFilePathUrl } = require('../../server/lib/plants');
|
||||
|
||||
describe('server plant image normalization', () => {
|
||||
it('accepts local public plant paths', () => {
|
||||
expect(normalizeImageUri('/plants/monstera-deliciosa.webp')).toBe('/plants/monstera-deliciosa.webp');
|
||||
expect(normalizeImageUri('plants/thumbs/monstera-deliciosa.webp')).toBe('/plants/thumbs/monstera-deliciosa.webp');
|
||||
});
|
||||
|
||||
it('rejects invalid local paths', () => {
|
||||
expect(normalizeImageUri('/uploads/monstera.webp')).toBeNull();
|
||||
expect(normalizeImageUri('/plants/../../secret.webp')).toBeNull();
|
||||
});
|
||||
|
||||
it('converts Wikimedia thumbnail URLs to stable file-path URLs for API responses', () => {
|
||||
expect(
|
||||
toWikimediaFilePathUrl('https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Agave_americana_%28detail%29.jpg/330px-Agave_americana_%28detail%29.jpg'),
|
||||
).toBe('https://commons.wikimedia.org/wiki/Special:FilePath/Agave_americana_(detail).jpg');
|
||||
});
|
||||
});
|
||||
const { normalizeImageUri, toWikimediaFilePathUrl } = require('../../server/lib/plants');
|
||||
|
||||
describe('server plant image normalization', () => {
|
||||
it('accepts local public plant paths', () => {
|
||||
expect(normalizeImageUri('/plants/monstera-deliciosa.webp')).toBe('/plants/monstera-deliciosa.webp');
|
||||
expect(normalizeImageUri('plants/thumbs/monstera-deliciosa.webp')).toBe('/plants/thumbs/monstera-deliciosa.webp');
|
||||
});
|
||||
|
||||
it('rejects invalid local paths', () => {
|
||||
expect(normalizeImageUri('/uploads/monstera.webp')).toBeNull();
|
||||
expect(normalizeImageUri('/plants/../../secret.webp')).toBeNull();
|
||||
});
|
||||
|
||||
it('converts Wikimedia thumbnail URLs to stable file-path URLs for API responses', () => {
|
||||
expect(
|
||||
toWikimediaFilePathUrl('https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Agave_americana_%28detail%29.jpg/330px-Agave_americana_%28detail%29.jpg'),
|
||||
).toBe('https://commons.wikimedia.org/wiki/Special:FilePath/Agave_americana_(detail).jpg');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,159 +1,159 @@
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const { closeDatabase, openDatabase, run } = require('../../server/lib/sqlite');
|
||||
const { ensurePlantSchema, getPlants } = require('../../server/lib/plants');
|
||||
|
||||
describe('server plant search ranking', () => {
|
||||
let db;
|
||||
let dbPath;
|
||||
|
||||
beforeAll(async () => {
|
||||
dbPath = path.join(os.tmpdir(), `greenlns-search-${Date.now()}.sqlite`);
|
||||
db = await openDatabase(dbPath);
|
||||
await ensurePlantSchema(db);
|
||||
|
||||
const entries = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Snake Plant',
|
||||
botanicalName: 'Sansevieria trifasciata',
|
||||
imageUri: '/plants/snake-plant.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Very resilient houseplant that handles little light well.',
|
||||
categories: ['easy', 'low_light', 'air_purifier'],
|
||||
careInfo: { waterIntervalDays: 14, light: 'Low to full light', temp: '16-30C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Spider Plant',
|
||||
botanicalName: 'Chlorophytum comosum',
|
||||
imageUri: '/plants/spider-plant.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Easy houseplant that is safe for pets and helps clean indoor air.',
|
||||
categories: ['easy', 'pet_friendly', 'air_purifier'],
|
||||
careInfo: { waterIntervalDays: 6, light: 'Bright to partial shade', temp: '16-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Monstera',
|
||||
botanicalName: 'Monstera deliciosa',
|
||||
imageUri: '/plants/monstera.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Popular indoor plant with large split leaves.',
|
||||
categories: ['easy'],
|
||||
careInfo: { waterIntervalDays: 7, light: 'Bright indirect light', temp: '18-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Easy Adan',
|
||||
botanicalName: 'Adan botanica',
|
||||
imageUri: '/plants/easy-adan.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Pet friendly plant for low light corners.',
|
||||
categories: ['succulent', 'low_light', 'pet_friendly'],
|
||||
careInfo: { waterIntervalDays: 8, light: 'Partial shade', temp: '18-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: 'Boston Fern',
|
||||
botanicalName: 'Nephrolepis exaltata',
|
||||
imageUri: '/plants/boston-fern.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Loves steady moisture and humid rooms.',
|
||||
categories: ['high_humidity', 'hanging'],
|
||||
careInfo: { waterIntervalDays: 3, light: 'Partial shade', temp: '16-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: 'Aloe Vera',
|
||||
botanicalName: 'Aloe vera',
|
||||
imageUri: '/plants/aloe-vera.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Sun-loving succulent for bright windows.',
|
||||
categories: ['succulent', 'sun', 'medicinal'],
|
||||
careInfo: { waterIntervalDays: 12, light: 'Sunny', temp: '18-30C' },
|
||||
confidence: 1,
|
||||
},
|
||||
];
|
||||
|
||||
for (const entry of entries) {
|
||||
await run(
|
||||
db,
|
||||
`INSERT INTO plants (
|
||||
id,
|
||||
name,
|
||||
botanicalName,
|
||||
imageUri,
|
||||
imageStatus,
|
||||
description,
|
||||
categories,
|
||||
careInfo,
|
||||
confidence,
|
||||
createdAt,
|
||||
updatedAt
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`,
|
||||
[
|
||||
entry.id,
|
||||
entry.name,
|
||||
entry.botanicalName,
|
||||
entry.imageUri,
|
||||
entry.imageStatus,
|
||||
entry.description,
|
||||
JSON.stringify(entry.categories),
|
||||
JSON.stringify(entry.careInfo),
|
||||
entry.confidence,
|
||||
],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (db) {
|
||||
await closeDatabase(db);
|
||||
}
|
||||
if (dbPath && fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns exact common name matches first', async () => {
|
||||
const results = await getPlants(db, { query: 'Monstera', limit: 3 });
|
||||
expect(results[0].name).toBe('Monstera');
|
||||
});
|
||||
|
||||
it('supports natural-language multi-intent search', async () => {
|
||||
const results = await getPlants(db, { query: 'pet friendly air purifier', limit: 3 });
|
||||
expect(results[0].name).toBe('Spider Plant');
|
||||
});
|
||||
|
||||
it('keeps empty-query category filtering intact', async () => {
|
||||
const results = await getPlants(db, { query: '', category: 'low_light', limit: 5 });
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
results.forEach((entry) => {
|
||||
expect(entry.categories).toContain('low_light');
|
||||
});
|
||||
});
|
||||
|
||||
it('applies category intersection together with semantic-style queries', async () => {
|
||||
const results = await getPlants(db, { query: 'easy', category: 'succulent', limit: 5 });
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('Easy Adan');
|
||||
});
|
||||
|
||||
it('maps bathroom-style queries to high-humidity plants', async () => {
|
||||
const results = await getPlants(db, { query: 'bathroom plant', limit: 3 });
|
||||
expect(results[0].name).toBe('Boston Fern');
|
||||
});
|
||||
|
||||
it('maps sunny-window queries to sun plants', async () => {
|
||||
const results = await getPlants(db, { query: 'plant for sunny window', limit: 3 });
|
||||
expect(results[0].name).toBe('Aloe Vera');
|
||||
});
|
||||
});
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const { closeDatabase, openDatabase, run } = require('../../server/lib/sqlite');
|
||||
const { ensurePlantSchema, getPlants } = require('../../server/lib/plants');
|
||||
|
||||
describe('server plant search ranking', () => {
|
||||
let db;
|
||||
let dbPath;
|
||||
|
||||
beforeAll(async () => {
|
||||
dbPath = path.join(os.tmpdir(), `greenlns-search-${Date.now()}.sqlite`);
|
||||
db = await openDatabase(dbPath);
|
||||
await ensurePlantSchema(db);
|
||||
|
||||
const entries = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Snake Plant',
|
||||
botanicalName: 'Sansevieria trifasciata',
|
||||
imageUri: '/plants/snake-plant.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Very resilient houseplant that handles little light well.',
|
||||
categories: ['easy', 'low_light', 'air_purifier'],
|
||||
careInfo: { waterIntervalDays: 14, light: 'Low to full light', temp: '16-30C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Spider Plant',
|
||||
botanicalName: 'Chlorophytum comosum',
|
||||
imageUri: '/plants/spider-plant.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Easy houseplant that is safe for pets and helps clean indoor air.',
|
||||
categories: ['easy', 'pet_friendly', 'air_purifier'],
|
||||
careInfo: { waterIntervalDays: 6, light: 'Bright to partial shade', temp: '16-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Monstera',
|
||||
botanicalName: 'Monstera deliciosa',
|
||||
imageUri: '/plants/monstera.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Popular indoor plant with large split leaves.',
|
||||
categories: ['easy'],
|
||||
careInfo: { waterIntervalDays: 7, light: 'Bright indirect light', temp: '18-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Easy Adan',
|
||||
botanicalName: 'Adan botanica',
|
||||
imageUri: '/plants/easy-adan.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Pet friendly plant for low light corners.',
|
||||
categories: ['succulent', 'low_light', 'pet_friendly'],
|
||||
careInfo: { waterIntervalDays: 8, light: 'Partial shade', temp: '18-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: 'Boston Fern',
|
||||
botanicalName: 'Nephrolepis exaltata',
|
||||
imageUri: '/plants/boston-fern.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Loves steady moisture and humid rooms.',
|
||||
categories: ['high_humidity', 'hanging'],
|
||||
careInfo: { waterIntervalDays: 3, light: 'Partial shade', temp: '16-24C' },
|
||||
confidence: 1,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: 'Aloe Vera',
|
||||
botanicalName: 'Aloe vera',
|
||||
imageUri: '/plants/aloe-vera.webp',
|
||||
imageStatus: 'ok',
|
||||
description: 'Sun-loving succulent for bright windows.',
|
||||
categories: ['succulent', 'sun', 'medicinal'],
|
||||
careInfo: { waterIntervalDays: 12, light: 'Sunny', temp: '18-30C' },
|
||||
confidence: 1,
|
||||
},
|
||||
];
|
||||
|
||||
for (const entry of entries) {
|
||||
await run(
|
||||
db,
|
||||
`INSERT INTO plants (
|
||||
id,
|
||||
name,
|
||||
botanicalName,
|
||||
imageUri,
|
||||
imageStatus,
|
||||
description,
|
||||
categories,
|
||||
careInfo,
|
||||
confidence,
|
||||
createdAt,
|
||||
updatedAt
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`,
|
||||
[
|
||||
entry.id,
|
||||
entry.name,
|
||||
entry.botanicalName,
|
||||
entry.imageUri,
|
||||
entry.imageStatus,
|
||||
entry.description,
|
||||
JSON.stringify(entry.categories),
|
||||
JSON.stringify(entry.careInfo),
|
||||
entry.confidence,
|
||||
],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (db) {
|
||||
await closeDatabase(db);
|
||||
}
|
||||
if (dbPath && fs.existsSync(dbPath)) {
|
||||
fs.unlinkSync(dbPath);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns exact common name matches first', async () => {
|
||||
const results = await getPlants(db, { query: 'Monstera', limit: 3 });
|
||||
expect(results[0].name).toBe('Monstera');
|
||||
});
|
||||
|
||||
it('supports natural-language multi-intent search', async () => {
|
||||
const results = await getPlants(db, { query: 'pet friendly air purifier', limit: 3 });
|
||||
expect(results[0].name).toBe('Spider Plant');
|
||||
});
|
||||
|
||||
it('keeps empty-query category filtering intact', async () => {
|
||||
const results = await getPlants(db, { query: '', category: 'low_light', limit: 5 });
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
results.forEach((entry) => {
|
||||
expect(entry.categories).toContain('low_light');
|
||||
});
|
||||
});
|
||||
|
||||
it('applies category intersection together with semantic-style queries', async () => {
|
||||
const results = await getPlants(db, { query: 'easy', category: 'succulent', limit: 5 });
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].name).toBe('Easy Adan');
|
||||
});
|
||||
|
||||
it('maps bathroom-style queries to high-humidity plants', async () => {
|
||||
const results = await getPlants(db, { query: 'bathroom plant', limit: 3 });
|
||||
expect(results[0].name).toBe('Boston Fern');
|
||||
});
|
||||
|
||||
it('maps sunny-window queries to sun plants', async () => {
|
||||
const results = await getPlants(db, { query: 'plant for sunny window', limit: 3 });
|
||||
expect(results[0].name).toBe('Aloe Vera');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
const { buildIdentifyPrompt, normalizeIdentifyResult } = require('../../server/lib/openai');
|
||||
const { applyCatalogGrounding, isLikelyGermanCommonName } = require('../../server/lib/scanGrounding');
|
||||
|
||||
describe('scan language guards', () => {
|
||||
it('keeps the English AI common name when the catalog match is obviously German', () => {
|
||||
const aiResult = {
|
||||
name: 'Poinsettia',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.66,
|
||||
description: 'Poinsettia was identified with AI. Care guidance is shown below.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 6,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
};
|
||||
|
||||
const catalogEntries = [
|
||||
{
|
||||
name: 'Weihnachtsstern',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Deutscher Katalogeintrag',
|
||||
careInfo: {
|
||||
waterIntervalDays: 8,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const grounded = applyCatalogGrounding(aiResult, catalogEntries, 'en');
|
||||
|
||||
expect(grounded.grounded).toBe(true);
|
||||
expect(grounded.result.name).toBe('Poinsettia');
|
||||
expect(grounded.result.botanicalName).toBe('Euphorbia pulcherrima');
|
||||
expect(grounded.result.description).toContain('identified with AI');
|
||||
expect(grounded.result.careInfo.light).toBe('Bright indirect light');
|
||||
expect(grounded.result.confidence).toBeGreaterThanOrEqual(0.78);
|
||||
});
|
||||
|
||||
it('keeps a botanical fallback name for English scans when the catalog name is German', () => {
|
||||
const normalized = normalizeIdentifyResult({
|
||||
name: 'Euphorbia pulcherrima',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.52,
|
||||
description: 'Euphorbia pulcherrima was identified with AI. Care guidance is shown below.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 7,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
}, 'en');
|
||||
|
||||
const grounded = applyCatalogGrounding(normalized, [
|
||||
{
|
||||
name: 'Weihnachtsstern',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Deutscher Katalogeintrag',
|
||||
careInfo: {
|
||||
waterIntervalDays: 9,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
], 'en');
|
||||
|
||||
expect(grounded.result.name).toBe('Euphorbia pulcherrima');
|
||||
expect(grounded.result.botanicalName).toBe('Euphorbia pulcherrima');
|
||||
});
|
||||
|
||||
it('does not regress non-English grounding behavior for Spanish', () => {
|
||||
const aiResult = {
|
||||
name: 'Poinsettia',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.64,
|
||||
description: 'La planta fue identificada con IA.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 6,
|
||||
light: 'Luz indirecta brillante',
|
||||
temp: '18-24C',
|
||||
},
|
||||
};
|
||||
|
||||
const grounded = applyCatalogGrounding(aiResult, [
|
||||
{
|
||||
name: 'Flor de Pascua',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Entrada de catalogo',
|
||||
careInfo: {
|
||||
waterIntervalDays: 7,
|
||||
light: 'Luz indirecta brillante',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
], 'es');
|
||||
|
||||
expect(grounded.result.name).toBe('Flor de Pascua');
|
||||
expect(grounded.result.description).toBe('La planta fue identificada con IA.');
|
||||
expect(grounded.result.careInfo.light).toBe('Luz indirecta brillante');
|
||||
});
|
||||
|
||||
it('hardens the English identify prompt against non-English common names', () => {
|
||||
const prompt = buildIdentifyPrompt('en', 'primary');
|
||||
|
||||
expect(prompt).toContain('English common name only');
|
||||
expect(prompt).toContain('Never return a German or other non-English common name');
|
||||
expect(prompt).toContain('use "botanicalName" as "name" instead');
|
||||
});
|
||||
|
||||
it('detects obviously German common names for override protection', () => {
|
||||
expect(isLikelyGermanCommonName('Weihnachtsstern')).toBe(true);
|
||||
expect(isLikelyGermanCommonName('Weinachtsstern')).toBe(true);
|
||||
expect(isLikelyGermanCommonName('Poinsettia')).toBe(false);
|
||||
});
|
||||
});
|
||||
const { buildIdentifyPrompt, normalizeIdentifyResult } = require('../../server/lib/openai');
|
||||
const { applyCatalogGrounding, isLikelyGermanCommonName } = require('../../server/lib/scanGrounding');
|
||||
|
||||
describe('scan language guards', () => {
|
||||
it('keeps the English AI common name when the catalog match is obviously German', () => {
|
||||
const aiResult = {
|
||||
name: 'Poinsettia',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.66,
|
||||
description: 'Poinsettia was identified with AI. Care guidance is shown below.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 6,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
};
|
||||
|
||||
const catalogEntries = [
|
||||
{
|
||||
name: 'Weihnachtsstern',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Deutscher Katalogeintrag',
|
||||
careInfo: {
|
||||
waterIntervalDays: 8,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const grounded = applyCatalogGrounding(aiResult, catalogEntries, 'en');
|
||||
|
||||
expect(grounded.grounded).toBe(true);
|
||||
expect(grounded.result.name).toBe('Poinsettia');
|
||||
expect(grounded.result.botanicalName).toBe('Euphorbia pulcherrima');
|
||||
expect(grounded.result.description).toContain('identified with AI');
|
||||
expect(grounded.result.careInfo.light).toBe('Bright indirect light');
|
||||
expect(grounded.result.confidence).toBeGreaterThanOrEqual(0.78);
|
||||
});
|
||||
|
||||
it('keeps a botanical fallback name for English scans when the catalog name is German', () => {
|
||||
const normalized = normalizeIdentifyResult({
|
||||
name: 'Euphorbia pulcherrima',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.52,
|
||||
description: 'Euphorbia pulcherrima was identified with AI. Care guidance is shown below.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 7,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
}, 'en');
|
||||
|
||||
const grounded = applyCatalogGrounding(normalized, [
|
||||
{
|
||||
name: 'Weihnachtsstern',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Deutscher Katalogeintrag',
|
||||
careInfo: {
|
||||
waterIntervalDays: 9,
|
||||
light: 'Bright indirect light',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
], 'en');
|
||||
|
||||
expect(grounded.result.name).toBe('Euphorbia pulcherrima');
|
||||
expect(grounded.result.botanicalName).toBe('Euphorbia pulcherrima');
|
||||
});
|
||||
|
||||
it('does not regress non-English grounding behavior for Spanish', () => {
|
||||
const aiResult = {
|
||||
name: 'Poinsettia',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
confidence: 0.64,
|
||||
description: 'La planta fue identificada con IA.',
|
||||
careInfo: {
|
||||
waterIntervalDays: 6,
|
||||
light: 'Luz indirecta brillante',
|
||||
temp: '18-24C',
|
||||
},
|
||||
};
|
||||
|
||||
const grounded = applyCatalogGrounding(aiResult, [
|
||||
{
|
||||
name: 'Flor de Pascua',
|
||||
botanicalName: 'Euphorbia pulcherrima',
|
||||
description: 'Entrada de catalogo',
|
||||
careInfo: {
|
||||
waterIntervalDays: 7,
|
||||
light: 'Luz indirecta brillante',
|
||||
temp: '18-24C',
|
||||
},
|
||||
},
|
||||
], 'es');
|
||||
|
||||
expect(grounded.result.name).toBe('Flor de Pascua');
|
||||
expect(grounded.result.description).toBe('La planta fue identificada con IA.');
|
||||
expect(grounded.result.careInfo.light).toBe('Luz indirecta brillante');
|
||||
});
|
||||
|
||||
it('hardens the English identify prompt against non-English common names', () => {
|
||||
const prompt = buildIdentifyPrompt('en', 'primary');
|
||||
|
||||
expect(prompt).toContain('English common name only');
|
||||
expect(prompt).toContain('Never return a German or other non-English common name');
|
||||
expect(prompt).toContain('use "botanicalName" as "name" instead');
|
||||
});
|
||||
|
||||
it('detects obviously German common names for override protection', () => {
|
||||
expect(isLikelyGermanCommonName('Weihnachtsstern')).toBe(true);
|
||||
expect(isLikelyGermanCommonName('Weinachtsstern')).toBe(true);
|
||||
expect(isLikelyGermanCommonName('Poinsettia')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { SEARCH_INTENT_CONFIG: rootConfig } = require('../../constants/searchIntentConfig');
|
||||
const { SEARCH_INTENT_CONFIG: serverConfig } = require('../../server/lib/searchIntentConfig');
|
||||
|
||||
describe('search intent config parity', () => {
|
||||
it('keeps root and server semantic intent config in sync', () => {
|
||||
expect(serverConfig).toEqual(rootConfig);
|
||||
});
|
||||
});
|
||||
const { SEARCH_INTENT_CONFIG: rootConfig } = require('../../constants/searchIntentConfig');
|
||||
const { SEARCH_INTENT_CONFIG: serverConfig } = require('../../server/lib/searchIntentConfig');
|
||||
|
||||
describe('search intent config parity', () => {
|
||||
it('keeps root and server semantic intent config in sync', () => {
|
||||
expect(serverConfig).toEqual(rootConfig);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user