centalize data model loading
This commit is contained in:
parent
3877e3a63e
commit
a778f5b5f7
@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data for validation
|
// Load tools data for validation
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
const tools = data.tools;
|
const tools = data.tools;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
const domainAgnosticSoftware = data['domain-agnostic-software'] || []; // Add this line
|
const domainAgnosticSoftware = data['domain-agnostic-software'] || []; // Add this line
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data
|
// Load tools data
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
|
|
||||||
const domains = data.domains;
|
const domains = data.domains;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
---
|
---
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data
|
// Load tools data
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
|
|
||||||
const domains = data.domains;
|
const domains = data.domains;
|
||||||
const phases = data.phases;
|
const phases = data.phases;
|
||||||
|
@ -2,9 +2,8 @@
|
|||||||
// src/pages/api/ai/query.ts
|
// src/pages/api/ai/query.ts
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import { getSessionFromRequest, verifySession } from '../../../utils/auth.js';
|
import { getSessionFromRequest, verifySession } from '../../../utils/auth.js';
|
||||||
import { promises as fs } from 'fs';
|
import { getCompressedToolsDataForAI } from '../../../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
@ -74,9 +73,7 @@ function checkRateLimit(userId: string): boolean {
|
|||||||
// Load tools database
|
// Load tools database
|
||||||
async function loadToolsDatabase() {
|
async function loadToolsDatabase() {
|
||||||
try {
|
try {
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
return await getCompressedToolsDataForAI();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
return load(yamlContent) as any;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load tools database:', error);
|
console.error('Failed to load tools database:', error);
|
||||||
throw new Error('Database unavailable');
|
throw new Error('Database unavailable');
|
||||||
@ -90,11 +87,11 @@ function createSystemPrompt(toolsData: any): string {
|
|||||||
description: tool.description,
|
description: tool.description,
|
||||||
domains: tool.domains,
|
domains: tool.domains,
|
||||||
phases: tool.phases,
|
phases: tool.phases,
|
||||||
|
domainAgnostic: tool['domain-agnostic-software'],
|
||||||
platforms: tool.platforms,
|
platforms: tool.platforms,
|
||||||
skillLevel: tool.skillLevel,
|
skillLevel: tool.skillLevel,
|
||||||
license: tool.license,
|
license: tool.license,
|
||||||
tags: tool.tags,
|
tags: tool.tags,
|
||||||
projectUrl: tool.projectUrl ? 'self-hosted' : 'external'
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Get regular phases (no more filtering needed)
|
// Get regular phases (no more filtering needed)
|
||||||
@ -119,16 +116,9 @@ function createSystemPrompt(toolsData: any): string {
|
|||||||
).join('\n');
|
).join('\n');
|
||||||
|
|
||||||
// Build dynamic phase descriptions for tool selection
|
// Build dynamic phase descriptions for tool selection
|
||||||
const phaseDescriptions = regularPhases.map((phase: any) => {
|
const phaseDescriptions = regularPhases.map((phase: any) =>
|
||||||
// Create generic descriptions or you could add a 'description' field to the YAML
|
`- ${phase.name}: ${phase.description || 'Tools for this phase'}`
|
||||||
const descriptions = {
|
).join('\n');
|
||||||
'data-collection': 'Imaging, Acquisition, Remote Collection Tools',
|
|
||||||
'examination': 'Parsing, Extraction, Initial Analysis Tools',
|
|
||||||
'analysis': 'Deep Analysis, Correlation, Visualization Tools',
|
|
||||||
'reporting': 'Documentation, Visualization, Presentation Tools (z.B. QGIS für Geodaten, Timeline-Tools)'
|
|
||||||
};
|
|
||||||
return `- ${phase.name}: ${phase.description || descriptions[phase.id] || 'Tools for this phase'}`;
|
|
||||||
}).join('\n');
|
|
||||||
|
|
||||||
// Add domain-agnostic software descriptions
|
// Add domain-agnostic software descriptions
|
||||||
const domainAgnosticDescriptions = domainAgnosticSoftware.map((section: any) =>
|
const domainAgnosticDescriptions = domainAgnosticSoftware.map((section: any) =>
|
||||||
@ -153,13 +143,13 @@ FORENSISCHE DOMÄNEN:
|
|||||||
${domainsDescription}
|
${domainsDescription}
|
||||||
|
|
||||||
WICHTIGE REGELN:
|
WICHTIGE REGELN:
|
||||||
1. Open Source Tools bevorzugen (license != "Proprietary")
|
1. Pro Phase 1-3 Tools empfehlen (immer mindestens 1 wenn verfügbar)
|
||||||
2. Pro Phase 1-3 Tools empfehlen (immer mindestens 1 wenn verfügbar)
|
2. Tools können in MEHREREN Phasen empfohlen werden wenn sinnvoll - versuche ein Tool für jede Phase zu empfehlen, selbst wenn die Priorität "low" ist.
|
||||||
3. Tools können in MEHREREN Phasen empfohlen werden wenn sinnvoll - versuche ein Tool für jede Phase zu empfehlen!
|
3. Für Reporting-Phase: Visualisierungs- und Dokumentationstools einschließen
|
||||||
4. Für Reporting-Phase: Visualisierungs- und Dokumentationstools einschließen
|
4. Gib stets dem spezieller für den Fall geeigneten Werkzeug den Vorzug.
|
||||||
5. Gib stets dem spezieller für den Fall geeigneten Werkzeug den Vorzug.
|
5. Deutsche Antworten für deutsche Anfragen, English for English queries
|
||||||
6. Deutsche Antworten für deutsche Anfragen, English for English queries
|
6. Bewerbe NIEMALS Proprietäre Software fälschlicherweise als Open-Source-Tools, erkenne aber an, falls diese besser geeignet sein könnte.
|
||||||
7. Bewerbe NIEMALS Proprietäre Software fälschlicherweise als Open-Source-Tools, erkenne aber an, falls diese besser geeignet sein könnte.
|
7. Bevorzuge alles, was nicht proprietär ist (license != "Proprietary"), aber erkenne an wenn ein proprietäres Tool besser geeignet ist.
|
||||||
|
|
||||||
TOOL-AUSWAHL NACH PHASE:
|
TOOL-AUSWAHL NACH PHASE:
|
||||||
${phaseDescriptions}
|
${phaseDescriptions}
|
||||||
|
@ -4,14 +4,11 @@ import ToolCard from '../components/ToolCard.astro';
|
|||||||
import ToolFilters from '../components/ToolFilters.astro';
|
import ToolFilters from '../components/ToolFilters.astro';
|
||||||
import ToolMatrix from '../components/ToolMatrix.astro';
|
import ToolMatrix from '../components/ToolMatrix.astro';
|
||||||
import AIQueryInterface from '../components/AIQueryInterface.astro';
|
import AIQueryInterface from '../components/AIQueryInterface.astro';
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data
|
// Load tools data
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
const tools = data.tools;
|
const tools = data.tools;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data
|
// Load tools data
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
|
|
||||||
// Filter tools that have knowledgebase entries
|
// Filter tools that have knowledgebase entries
|
||||||
const knowledgebaseTools = data.tools.filter((tool: any) => tool.knowledgebase === true);
|
const knowledgebaseTools = data.tools.filter((tool: any) => tool.knowledgebase === true);
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import { promises as fs } from 'fs';
|
import { getToolsData } from '../utils/dataService.js';
|
||||||
import { load } from 'js-yaml';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
// Load tools data to get self-hosted services
|
// Load tools data to get self-hosted services
|
||||||
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
const data = await getToolsData();
|
||||||
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
||||||
const data = load(yamlContent) as any;
|
|
||||||
|
|
||||||
// Filter for hosted services based on projectUrl presence
|
// Filter for hosted services based on projectUrl presence
|
||||||
const hostedServices = data.tools.filter((tool: any) => {
|
const hostedServices = data.tools.filter((tool: any) => {
|
||||||
|
111
src/utils/dataService.ts
Normal file
111
src/utils/dataService.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import { load } from 'js-yaml';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
interface ToolsData {
|
||||||
|
tools: any[];
|
||||||
|
domains: any[];
|
||||||
|
phases: any[];
|
||||||
|
'domain-agnostic-software': any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CompressedToolsData extends Omit<ToolsData, 'tools'> {
|
||||||
|
tools: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedData: ToolsData | null = null;
|
||||||
|
let cachedRandomizedData: ToolsData | null = null;
|
||||||
|
let cachedCompressedData: CompressedToolsData | null = null;
|
||||||
|
let lastRandomizationDate: string | null = null;
|
||||||
|
|
||||||
|
// Create a seeded random number generator
|
||||||
|
function seededRandom(seed: number): () => number {
|
||||||
|
let x = Math.sin(seed) * 10000;
|
||||||
|
return function() {
|
||||||
|
x = Math.sin(x) * 10000;
|
||||||
|
return x - Math.floor(x);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get today's date as seed + process start time for consistency within day/session
|
||||||
|
function getDailySeed(): number {
|
||||||
|
const today = new Date().toDateString();
|
||||||
|
const processStart = process.uptime();
|
||||||
|
return today.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) + Math.floor(processStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fisher-Yates shuffle with seeded random
|
||||||
|
function shuffleArray<T>(array: T[], randomFn: () => number): T[] {
|
||||||
|
const shuffled = [...array];
|
||||||
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(randomFn() * (i + 1));
|
||||||
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||||
|
}
|
||||||
|
return shuffled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load raw data from YAML
|
||||||
|
async function loadRawData(): Promise<ToolsData> {
|
||||||
|
if (!cachedData) {
|
||||||
|
const yamlPath = path.join(process.cwd(), 'src/data/tools.yaml');
|
||||||
|
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
||||||
|
cachedData = load(yamlContent) as ToolsData;
|
||||||
|
}
|
||||||
|
return cachedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tools data with randomized tool order (daily seed)
|
||||||
|
export async function getToolsData(): Promise<ToolsData> {
|
||||||
|
const today = new Date().toDateString();
|
||||||
|
|
||||||
|
// Check if we need to re-randomize (new day or first load)
|
||||||
|
if (!cachedRandomizedData || lastRandomizationDate !== today) {
|
||||||
|
const rawData = await loadRawData();
|
||||||
|
const seed = getDailySeed();
|
||||||
|
const randomFn = seededRandom(seed);
|
||||||
|
|
||||||
|
// Randomize tools array while keeping other data intact
|
||||||
|
const randomizedTools = shuffleArray(rawData.tools, randomFn);
|
||||||
|
|
||||||
|
cachedRandomizedData = {
|
||||||
|
...rawData,
|
||||||
|
tools: randomizedTools
|
||||||
|
};
|
||||||
|
|
||||||
|
lastRandomizationDate = today;
|
||||||
|
|
||||||
|
// Clear compressed cache when we re-randomize
|
||||||
|
cachedCompressedData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedRandomizedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get compressed data for AI (removes projectUrl and statusUrl)
|
||||||
|
export async function getCompressedToolsDataForAI(): Promise<CompressedToolsData> {
|
||||||
|
if (!cachedCompressedData) {
|
||||||
|
const data = await getToolsData();
|
||||||
|
|
||||||
|
const compressedTools = data.tools.map(tool => {
|
||||||
|
const { projectUrl, statusUrl, ...compressedTool } = tool;
|
||||||
|
return compressedTool;
|
||||||
|
});
|
||||||
|
|
||||||
|
cachedCompressedData = {
|
||||||
|
tools: compressedTools,
|
||||||
|
domains: data.domains,
|
||||||
|
phases: data.phases,
|
||||||
|
'domain-agnostic-software': data['domain-agnostic-software']
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedCompressedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force cache refresh (useful for development)
|
||||||
|
export function clearCache(): void {
|
||||||
|
cachedData = null;
|
||||||
|
cachedRandomizedData = null;
|
||||||
|
cachedCompressedData = null;
|
||||||
|
lastRandomizationDate = null;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user