feat: sync runtime search and schema quality updates from app repo
- port retrieval, validation, and eval improvements relevant to os - align prompts and dimensions with the flat single-agent model - replace the old eval suite with the focused core scenarios Generated with Codex
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
import { getSQLiteClient } from './sqlite-client';
|
||||
import { openai as openaiProvider } from '@ai-sdk/openai';
|
||||
import { generateText } from 'ai';
|
||||
import { hasValidOpenAiKey } from '../storage/apiKeys';
|
||||
|
||||
export interface Dimension {
|
||||
name: string;
|
||||
@@ -18,85 +15,23 @@ export interface LockedDimension {
|
||||
|
||||
export class DimensionService {
|
||||
/**
|
||||
* Get all locked (priority) dimensions with their descriptions
|
||||
* Legacy compatibility shim. Dimensions are now flat, so there is no locked subset.
|
||||
*/
|
||||
static async getLockedDimensions(): Promise<LockedDimension[]> {
|
||||
const sqlite = getSQLiteClient();
|
||||
|
||||
const result = sqlite.query(`
|
||||
WITH dimension_counts AS (
|
||||
SELECT nd.dimension, COUNT(*) AS count
|
||||
FROM node_dimensions nd
|
||||
GROUP BY nd.dimension
|
||||
)
|
||||
SELECT
|
||||
d.name,
|
||||
d.description,
|
||||
COALESCE(dc.count, 0) AS count
|
||||
FROM dimensions d
|
||||
LEFT JOIN dimension_counts dc ON dc.dimension = d.name
|
||||
WHERE d.is_priority = 1
|
||||
ORDER BY d.name ASC
|
||||
`);
|
||||
|
||||
return result.rows.map((row: any) => ({
|
||||
name: row.name,
|
||||
description: row.description,
|
||||
count: Number(row.count)
|
||||
}));
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically assign locked dimensions + suggest keyword dimensions
|
||||
* Returns { locked: string[], keywords: string[] }
|
||||
*
|
||||
* IMPORTANT: Returns empty result immediately if no valid API key is configured.
|
||||
* This prevents slow node creation when OpenAI is unavailable.
|
||||
* Automatic special-dimension assignment has been removed. Callers must provide dimensions explicitly.
|
||||
*/
|
||||
static async assignDimensions(nodeData: {
|
||||
title: string;
|
||||
notes?: string;
|
||||
content?: string;
|
||||
link?: string;
|
||||
description?: string;
|
||||
}): Promise<{ locked: string[]; keywords: string[] }> {
|
||||
// Fast path: skip AI if no valid API key
|
||||
if (!hasValidOpenAiKey()) {
|
||||
console.log(`[DimensionAssignment] No valid OpenAI key, skipping for: "${nodeData.title}"`);
|
||||
return { locked: [], keywords: [] };
|
||||
}
|
||||
|
||||
try {
|
||||
const lockedDimensions = await this.getLockedDimensions();
|
||||
|
||||
if (lockedDimensions.length === 0) {
|
||||
console.log('[DimensionAssignment] No locked dimensions available');
|
||||
return { locked: [], keywords: [] };
|
||||
}
|
||||
|
||||
const prompt = this.buildAssignmentPrompt(nodeData, lockedDimensions);
|
||||
|
||||
console.log(`[DimensionAssignment] Processing: "${nodeData.title}"`);
|
||||
|
||||
const response = await generateText({
|
||||
model: openaiProvider('gpt-4o-mini'),
|
||||
prompt,
|
||||
maxOutputTokens: 300, // Increased to accommodate more dimensions
|
||||
temperature: 0.1,
|
||||
});
|
||||
|
||||
console.log(`[DimensionAssignment] AI Response:\n${response.text}`);
|
||||
|
||||
const result = this.parseAssignmentResponse(response.text, lockedDimensions);
|
||||
|
||||
console.log(`[DimensionAssignment] Locked: ${result.locked.join(', ') || 'none'}`);
|
||||
console.log(`[DimensionAssignment] Keywords: ${result.keywords.join(', ') || 'none'}`);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DimensionAssignment] Error:', error);
|
||||
return { locked: [], keywords: [] };
|
||||
}
|
||||
console.log(`[DimensionAssignment] Skipped for "${nodeData.title}" — flat dimensions require explicit assignment.`);
|
||||
return { locked: [], keywords: [] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,7 +88,7 @@ export class DimensionService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Build AI prompt for dimension assignment (locked dimensions only)
|
||||
* Legacy no-op prompt builder retained only for backward compatibility.
|
||||
*/
|
||||
private static buildAssignmentPrompt(
|
||||
nodeData: { title: string; notes?: string; link?: string; description?: string },
|
||||
@@ -162,13 +97,13 @@ export class DimensionService {
|
||||
// Use description as primary context, content as fallback
|
||||
let nodeContextSection: string;
|
||||
if (nodeData.description) {
|
||||
const contentPreview = nodeData.notes?.slice(0, 500) || '';
|
||||
const notesPreview = nodeData.notes?.slice(0, 500) || '';
|
||||
nodeContextSection = `DESCRIPTION: ${nodeData.description}
|
||||
|
||||
NOTES PREVIEW: ${contentPreview}${nodeData.notes && nodeData.notes.length > 500 ? '...' : ''}`;
|
||||
NOTES PREVIEW: ${notesPreview}${nodeData.notes && nodeData.notes.length > 500 ? '...' : ''}`;
|
||||
} else {
|
||||
const contentPreview = nodeData.notes?.slice(0, 2000) || '';
|
||||
nodeContextSection = `NOTES: ${contentPreview}${nodeData.notes && nodeData.notes.length > 2000 ? '...' : ''}`;
|
||||
const notesPreview = nodeData.notes?.slice(0, 2000) || '';
|
||||
nodeContextSection = `NOTES: ${notesPreview}${nodeData.notes && nodeData.notes.length > 2000 ? '...' : ''}`;
|
||||
}
|
||||
|
||||
// Include ALL locked dimensions, using fallback text for those without descriptions
|
||||
@@ -181,7 +116,7 @@ NOTES PREVIEW: ${contentPreview}${nodeData.notes && nodeData.notes.length > 500
|
||||
})
|
||||
.join('\n---\n');
|
||||
|
||||
return `You are categorizing a knowledge node into locked dimensions.
|
||||
return `Dimensions are now flat categories with no locked subset.
|
||||
|
||||
=== NODE TO CATEGORIZE ===
|
||||
Title: ${nodeData.title}
|
||||
@@ -203,7 +138,7 @@ LOCKED:
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse AI response and extract locked dimensions
|
||||
* Legacy no-op parser retained only for backward compatibility.
|
||||
*/
|
||||
private static parseAssignmentResponse(
|
||||
response: string,
|
||||
@@ -251,4 +186,4 @@ LOCKED:
|
||||
}
|
||||
}
|
||||
|
||||
export const dimensionService = new DimensionService();
|
||||
export const dimensionService = new DimensionService();
|
||||
|
||||
Reference in New Issue
Block a user