feat: finalize local OpenAI key flow and MCP docs

- add server-side .env.local OpenAI key management for the open-source app
- route AI features through the preferred local key path and lazy-load embed recovery
- rewrite README and docs for current MCP setup, schema, and fully-local guidance

Generated with Claude Code
This commit is contained in:
“BeeRad”
2026-04-16 14:08:37 +10:00
parent c2f880d957
commit 97eeb0789f
28 changed files with 891 additions and 215 deletions
+3 -2
View File
@@ -1,11 +1,11 @@
import { tool } from 'ai';
import { z } from 'zod';
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { extractPaper } from '@/services/typescript/extractors/paper';
import { getInternalApiBaseUrl } from '@/services/runtime/apiBase';
import { formatNodeForChat } from '../infrastructure/nodeFormatter';
import { validateExplicitDescription } from '@/services/database/quality';
import { createLocalOpenAIProvider } from '@/services/openai/localProvider';
function ensureNodeDescription(candidate: string | undefined, fallbackLead: string): string {
const normalizedCandidate = typeof candidate === 'string'
@@ -25,6 +25,7 @@ function ensureNodeDescription(candidate: string | undefined, fallbackLead: stri
// AI-powered content analysis
async function analyzeContentWithAI(title: string, description: string, contentType: string) {
try {
const provider = createLocalOpenAIProvider();
const prompt = `Analyze this ${contentType} content and provide classification.
Title: "${title}"
@@ -57,7 +58,7 @@ Respond with ONLY valid JSON (no markdown, no code blocks):
}`;
const response = await generateText({
model: openai('gpt-4o-mini'),
model: provider('gpt-4o-mini'),
prompt,
maxOutputTokens: 800
});
+3 -2
View File
@@ -1,11 +1,11 @@
import { tool } from 'ai';
import { z } from 'zod';
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { extractWebsite } from '@/services/typescript/extractors/website';
import { getInternalApiBaseUrl } from '@/services/runtime/apiBase';
import { formatNodeForChat } from '../infrastructure/nodeFormatter';
import { validateExplicitDescription } from '@/services/database/quality';
import { createLocalOpenAIProvider } from '@/services/openai/localProvider';
function ensureNodeDescription(candidate: string | undefined, fallbackLead: string): string {
const normalizedCandidate = typeof candidate === 'string'
@@ -40,6 +40,7 @@ async function analyzeContentWithAI(
contentType: string
) {
try {
const provider = createLocalOpenAIProvider();
const prompt = `Analyze this ${contentType} content and provide classification.
Title: "${title}"
@@ -71,7 +72,7 @@ Respond with ONLY valid JSON (no markdown, no code blocks):
}`;
const response = await generateText({
model: openai('gpt-4o-mini'),
model: provider('gpt-4o-mini'),
prompt,
maxOutputTokens: 800
});
+5 -3
View File
@@ -1,11 +1,11 @@
import { tool } from 'ai';
import { z } from 'zod';
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { extractYouTube } from '@/services/typescript/extractors/youtube';
import { getInternalApiBaseUrl } from '@/services/runtime/apiBase';
import { formatNodeForChat } from '../infrastructure/nodeFormatter';
import { validateExplicitDescription } from '@/services/database/quality';
import { createLocalOpenAIProvider } from '@/services/openai/localProvider';
function ensureNodeDescription(candidate: string | undefined, fallbackLead: string): string {
const normalizedCandidate = typeof candidate === 'string'
@@ -29,6 +29,7 @@ async function analyzeContentWithAI(
contentType: string
) {
try {
const provider = createLocalOpenAIProvider();
const prompt = `Analyze this ${contentType} content and provide classification.
Title: "${title}"
@@ -60,7 +61,7 @@ Respond with ONLY valid JSON (no markdown, no code blocks):
}`;
const response = await generateText({
model: openai('gpt-4o-mini'),
model: provider('gpt-4o-mini'),
prompt,
maxOutputTokens: 800
});
@@ -113,8 +114,9 @@ ${excerpt}
`;
try {
const provider = createLocalOpenAIProvider();
const response = await generateText({
model: openai('gpt-4o-mini'),
model: provider('gpt-4o-mini'),
prompt,
maxOutputTokens: 400
});