docs: add complete guide, env files, fix docker-compose

- Add GUIDE.md: complete user documentation covering installation,
  Docker deployment, AI providers, MCP server, N8N integration,
  email config, admin panel, env var reference, troubleshooting
- Add mcp-server/.env.example with all MCP-specific variables
- Update .env.docker.example with all 42 environment variables
- Fix docker-compose.yml: parameterize PostgreSQL credentials,
  add missing env vars (CUSTOM_OPENAI, AI_PROVIDER_CHAT,
  ALLOW_REGISTRATION, RESEND_API_KEY)
- Track memento-note/.env.example
This commit is contained in:
Sepehr Ramezani
2026-04-20 22:57:09 +02:00
parent e4d4e23dc7
commit 5b7cbcbc49
23 changed files with 1054 additions and 996 deletions

View File

@@ -40,17 +40,17 @@ export class CustomOpenAIProvider implements AIProvider {
model: this.model,
schema: z.object({
tags: z.array(z.object({
tag: z.string().describe('Le nom du tag, court et en minuscules'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
tag: z.string().describe('Short tag name in lowercase'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: `Analyse la note suivante et suggère entre 1 et 5 tags pertinents.
Contenu de la note: "${content}"`,
prompt: `Analyze the following note and suggest 1 to 5 relevant tags.
Note content: "${content}"`,
});
return object.tags;
} catch (e) {
console.error('Erreur génération tags Custom OpenAI:', e);
console.error('Error generating tags (Custom OpenAI):', e);
return [];
}
}
@@ -63,7 +63,7 @@ export class CustomOpenAIProvider implements AIProvider {
});
return embedding;
} catch (e) {
console.error('Erreur embeddings Custom OpenAI:', e);
console.error('Error generating embeddings (Custom OpenAI):', e);
return [];
}
}
@@ -74,8 +74,8 @@ export class CustomOpenAIProvider implements AIProvider {
model: this.model,
schema: z.object({
titles: z.array(z.object({
title: z.string().describe('Le titre suggéré'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
title: z.string().describe('Suggested title'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: prompt,
@@ -83,7 +83,7 @@ export class CustomOpenAIProvider implements AIProvider {
return object.titles;
} catch (e) {
console.error('Erreur génération titres Custom OpenAI:', e);
console.error('Error generating titles (Custom OpenAI):', e);
return [];
}
}
@@ -97,7 +97,7 @@ export class CustomOpenAIProvider implements AIProvider {
return text.trim();
} catch (e) {
console.error('Erreur génération texte Custom OpenAI:', e);
console.error('Error generating text (Custom OpenAI):', e);
throw e;
}
}
@@ -112,7 +112,7 @@ export class CustomOpenAIProvider implements AIProvider {
return { text: text.trim() };
} catch (e) {
console.error('Erreur chat Custom OpenAI:', e);
console.error('Error in chat (Custom OpenAI):', e);
throw e;
}
}

View File

@@ -24,17 +24,17 @@ export class DeepSeekProvider implements AIProvider {
model: this.model,
schema: z.object({
tags: z.array(z.object({
tag: z.string().describe('Le nom du tag, court et en minuscules'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
tag: z.string().describe('Short tag name in lowercase'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: `Analyse la note suivante et suggère entre 1 et 5 tags pertinents.
Contenu de la note: "${content}"`,
prompt: `Analyze the following note and suggest 1 to 5 relevant tags.
Note content: "${content}"`,
});
return object.tags;
} catch (e) {
console.error('Erreur génération tags DeepSeek:', e);
console.error('Error generating tags (DeepSeek):', e);
return [];
}
}
@@ -47,7 +47,7 @@ export class DeepSeekProvider implements AIProvider {
});
return embedding;
} catch (e) {
console.error('Erreur embeddings DeepSeek:', e);
console.error('Error generating embeddings (DeepSeek):', e);
return [];
}
}
@@ -58,8 +58,8 @@ export class DeepSeekProvider implements AIProvider {
model: this.model,
schema: z.object({
titles: z.array(z.object({
title: z.string().describe('Le titre suggéré'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
title: z.string().describe('Suggested title'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: prompt,
@@ -67,7 +67,7 @@ export class DeepSeekProvider implements AIProvider {
return object.titles;
} catch (e) {
console.error('Erreur génération titres DeepSeek:', e);
console.error('Error generating titles (DeepSeek):', e);
return [];
}
}
@@ -81,7 +81,7 @@ export class DeepSeekProvider implements AIProvider {
return text.trim();
} catch (e) {
console.error('Erreur génération texte DeepSeek:', e);
console.error('Error generating text (DeepSeek):', e);
throw e;
}
}
@@ -96,7 +96,7 @@ export class DeepSeekProvider implements AIProvider {
return { text: text.trim() };
} catch (e) {
console.error('Erreur chat DeepSeek:', e);
console.error('Error in chat (DeepSeek):', e);
throw e;
}
}

View File

@@ -75,7 +75,7 @@ Note content: "${content}"`;
return JSON.parse(jsonMatch[0]);
}
// Support pour le format { "tags": [...] }
// Support for { "tags": [...] } format
const objectMatch = text.match(/\{\s*"tags"\s*:\s*(\[[\s\S]*\])\s*\}/);
if (objectMatch && objectMatch[1]) {
return JSON.parse(objectMatch[1]);
@@ -83,7 +83,7 @@ Note content: "${content}"`;
return [];
} catch (e) {
console.error('Erreur API directe Ollama:', e);
console.error('Error in Ollama API:', e);
return [];
}
}
@@ -104,7 +104,7 @@ Note content: "${content}"`;
const data = await response.json();
return data.embedding;
} catch (e) {
console.error('Erreur embeddings directs Ollama:', e);
console.error('Error generating embeddings (Ollama):', e);
return [];
}
}
@@ -116,7 +116,7 @@ Note content: "${content}"`;
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: this.modelName,
prompt: `${prompt}\n\nRéponds UNIQUEMENT sous forme de tableau JSON : [{"title": "string", "confidence": number}]`,
prompt: `${prompt}\n\nRespond ONLY as a JSON array: [{"title": "string", "confidence": number}]`,
stream: false,
}),
});
@@ -126,7 +126,7 @@ Note content: "${content}"`;
const data = await response.json();
const text = data.response;
// Extraire le JSON de la réponse
// Extract JSON from response
const jsonMatch = text.match(/\[\s*\{[\s\S]*\}\s*\]/);
if (jsonMatch) {
return JSON.parse(jsonMatch[0]);
@@ -134,7 +134,7 @@ Note content: "${content}"`;
return [];
} catch (e) {
console.error('Erreur génération titres Ollama:', e);
console.error('Error generating titles (Ollama):', e);
return [];
}
}
@@ -156,7 +156,7 @@ Note content: "${content}"`;
const data = await response.json();
return data.response.trim();
} catch (e) {
console.error('Erreur génération texte Ollama:', e);
console.error('Error generating text (Ollama):', e);
throw e;
}
}
@@ -187,7 +187,7 @@ Note content: "${content}"`;
const data = await response.json();
return { text: data.message?.content?.trim() || '' };
} catch (e) {
console.error('Erreur chat Ollama:', e);
console.error('Error in chat (Ollama):', e);
throw e;
}
}

View File

@@ -24,17 +24,17 @@ export class OpenAIProvider implements AIProvider {
model: this.model,
schema: z.object({
tags: z.array(z.object({
tag: z.string().describe('Le nom du tag, court et en minuscules'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
tag: z.string().describe('Short tag name in lowercase'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: `Analyse la note suivante et suggère entre 1 et 5 tags pertinents.
Contenu de la note: "${content}"`,
prompt: `Analyze the following note and suggest 1 to 5 relevant tags.
Note content: "${content}"`,
});
return object.tags;
} catch (e) {
console.error('Erreur génération tags OpenAI:', e);
console.error('Error generating tags (OpenAI):', e);
return [];
}
}
@@ -47,7 +47,7 @@ export class OpenAIProvider implements AIProvider {
});
return embedding;
} catch (e) {
console.error('Erreur embeddings OpenAI:', e);
console.error('Error generating embeddings (OpenAI):', e);
return [];
}
}
@@ -58,8 +58,8 @@ export class OpenAIProvider implements AIProvider {
model: this.model,
schema: z.object({
titles: z.array(z.object({
title: z.string().describe('Le titre suggéré'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
title: z.string().describe('Suggested title'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: prompt,
@@ -67,7 +67,7 @@ export class OpenAIProvider implements AIProvider {
return object.titles;
} catch (e) {
console.error('Erreur génération titres OpenAI:', e);
console.error('Error generating titles (OpenAI):', e);
return [];
}
}
@@ -81,7 +81,7 @@ export class OpenAIProvider implements AIProvider {
return text.trim();
} catch (e) {
console.error('Erreur génération texte OpenAI:', e);
console.error('Error generating text (OpenAI):', e);
throw e;
}
}
@@ -96,7 +96,7 @@ export class OpenAIProvider implements AIProvider {
return { text: text.trim() };
} catch (e) {
console.error('Erreur chat OpenAI:', e);
console.error('Error in chat (OpenAI):', e);
throw e;
}
}

View File

@@ -24,17 +24,17 @@ export class OpenRouterProvider implements AIProvider {
model: this.model,
schema: z.object({
tags: z.array(z.object({
tag: z.string().describe('Le nom du tag, court et en minuscules'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
tag: z.string().describe('Short tag name in lowercase'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: `Analyse la note suivante et suggère entre 1 et 5 tags pertinents.
Contenu de la note: "${content}"`,
prompt: `Analyze the following note and suggest 1 to 5 relevant tags.
Note content: "${content}"`,
});
return object.tags;
} catch (e) {
console.error('Erreur génération tags OpenRouter:', e);
console.error('Error generating tags (OpenRouter):', e);
return [];
}
}
@@ -47,7 +47,7 @@ export class OpenRouterProvider implements AIProvider {
});
return embedding;
} catch (e) {
console.error('Erreur embeddings OpenRouter:', e);
console.error('Error generating embeddings (OpenRouter):', e);
return [];
}
}
@@ -58,8 +58,8 @@ export class OpenRouterProvider implements AIProvider {
model: this.model,
schema: z.object({
titles: z.array(z.object({
title: z.string().describe('Le titre suggéré'),
confidence: z.number().min(0).max(1).describe('Le niveau de confiance entre 0 et 1')
title: z.string().describe('Suggested title'),
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
}))
}),
prompt: prompt,
@@ -67,7 +67,7 @@ export class OpenRouterProvider implements AIProvider {
return object.titles;
} catch (e) {
console.error('Erreur génération titres OpenRouter:', e);
console.error('Error generating titles (OpenRouter):', e);
return [];
}
}
@@ -81,7 +81,7 @@ export class OpenRouterProvider implements AIProvider {
return text.trim();
} catch (e) {
console.error('Erreur génération texte OpenRouter:', e);
console.error('Error generating text (OpenRouter):', e);
throw e;
}
}
@@ -96,7 +96,7 @@ export class OpenRouterProvider implements AIProvider {
return { text: text.trim() };
} catch (e) {
console.error('Erreur chat OpenRouter:', e);
console.error('Error in chat (OpenRouter):', e);
throw e;
}
}

View File

@@ -29,32 +29,32 @@ export interface ToolCallResult {
export interface AIProvider {
/**
* Analyse le contenu et suggère des tags pertinents.
* Analyze content and suggest relevant tags.
*/
generateTags(content: string, language?: string): Promise<TagSuggestion[]>;
/**
* Génère un vecteur d'embeddings pour la recherche sémantique.
* Generate an embedding vector for semantic search.
*/
getEmbeddings(text: string): Promise<number[]>;
/**
* Génère des suggestions de titres basées sur le contenu.
* Generate title suggestions based on content.
*/
generateTitles(prompt: string): Promise<TitleSuggestion[]>;
/**
* Génère du texte basé sur un prompt.
* Generate text based on a prompt.
*/
generateText(prompt: string): Promise<string>;
/**
* Fournit une réponse de chat (utilisé pour le système agentique)
* Provide a chat response (used for the agentic system)
*/
chat(messages: any[], systemPrompt?: string): Promise<any>;
/**
* Retourne le modèle AI SDK pour le streaming direct (utilisé par l'API route)
* Return the AI SDK model for direct streaming (used by API route)
*/
getModel(): any;
@@ -69,7 +69,7 @@ export type AIProviderType = 'openai' | 'ollama';
export interface AIConfig {
provider: AIProviderType;
apiKey?: string;
baseUrl?: string; // Utile pour Ollama
baseUrl?: string; // Used for Ollama
model?: string;
embeddingModel?: string;
}