fix(python): Entfernt inkompatibles response_mime_type Argument
Behebt den TypeError beim Aufruf von GenerationConfig in der älteren Version der google-generativeai Bibliothek, indem das nicht unterstützte Argument entfernt wird.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -59,3 +59,5 @@ ngrok
|
|||||||
*screenshot.png
|
*screenshot.png
|
||||||
auth_output.txt
|
auth_output.txt
|
||||||
auth_url.txt
|
auth_url.txt
|
||||||
|
\ngemini_api_key.txt
|
||||||
|
.venv/
|
||||||
|
|||||||
Binary file not shown.
1
general-market-intelligence/.gitignore
vendored
1
general-market-intelligence/.gitignore
vendored
@@ -22,3 +22,4 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
\ntmp/
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
},
|
"start-backend": "node server.cjs"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@@ -16,7 +17,10 @@
|
|||||||
"uuid": "^13.0.0",
|
"uuid": "^13.0.0",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"jspdf-autotable": "^3.8.1"
|
"jspdf-autotable": "^3.8.1",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"body-parser": "^1.20.2",
|
||||||
|
"cors": "^2.8.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.14.0",
|
||||||
|
|||||||
@@ -38,7 +38,10 @@ app.post('/api/generate-search-strategy', async (req, res) => {
|
|||||||
// Aktuell gehen wir davon aus, dass das Python-Skript im Hauptverzeichnis liegt.
|
// Aktuell gehen wir davon aus, dass das Python-Skript im Hauptverzeichnis liegt.
|
||||||
const pythonProcess = spawn(
|
const pythonProcess = spawn(
|
||||||
path.join(__dirname, '..', '.venv', 'bin', 'python3'), // Pfad zur venv python3
|
path.join(__dirname, '..', '.venv', 'bin', 'python3'), // Pfad zur venv python3
|
||||||
[path.join(__dirname, '..', 'market_intel_orchestrator.py'), '--mode', 'generate_strategy', '--reference_url', referenceUrl, '--context_file', tempContextFilePath]
|
[path.join(__dirname, '..', 'market_intel_orchestrator.py'), '--mode', 'generate_strategy', '--reference_url', referenceUrl, '--context_file', tempContextFilePath],
|
||||||
|
{
|
||||||
|
env: { ...process.env, PYTHONPATH: path.join(__dirname, '..', '.venv', 'lib', 'python3.11', 'site-packages') }
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let pythonOutput = '';
|
let pythonOutput = '';
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
|
||||||
import { GoogleGenAI } from "@google/genai";
|
|
||||||
import { LeadStatus, AnalysisResult, Competitor, Language, Tier, EmailDraft, SearchStrategy, SearchSignal } from "../types";
|
import { LeadStatus, AnalysisResult, Competitor, Language, Tier, EmailDraft, SearchStrategy, SearchSignal } from "../types";
|
||||||
|
|
||||||
const apiKey = process.env.API_KEY;
|
// const apiKey = process.env.API_KEY; // Nicht mehr direkt im Frontend verwendet
|
||||||
const ai = new GoogleGenAI({ apiKey: apiKey || '' });
|
// const ai = new GoogleGenAI({ apiKey: apiKey || '' }); // Nicht mehr direkt im Frontend verwendet
|
||||||
|
|
||||||
// Helper to extract JSON
|
// URL unserer lokalen Node.js API-Brücke
|
||||||
|
const API_BASE_URL = `http://${window.location.hostname}:3001/api`;
|
||||||
|
|
||||||
|
// Helper to extract JSON (kann ggf. entfernt werden, wenn das Backend immer sauberes JSON liefert)
|
||||||
const extractJson = (text: string): any => {
|
const extractJson = (text: string): any => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(text);
|
return JSON.parse(text);
|
||||||
@@ -27,219 +28,71 @@ const extractJson = (text: string): any => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NEW: Generates a search strategy based on the uploaded strategy file and reference URL.
|
* NEU: Ruft unser Python-Backend über die Node.js API-Brücke auf.
|
||||||
*/
|
*/
|
||||||
export const generateSearchStrategy = async (
|
export const generateSearchStrategy = async (
|
||||||
referenceUrl: string,
|
referenceUrl: string,
|
||||||
contextContent: string,
|
contextContent: string,
|
||||||
language: Language
|
language: Language
|
||||||
): Promise<SearchStrategy> => {
|
): Promise<SearchStrategy> => {
|
||||||
if (!apiKey) throw new Error("API Key missing");
|
// API-Key wird jetzt vom Backend verwaltet
|
||||||
|
|
||||||
const langInstruction = language === 'de' ? "OUTPUT LANGUAGE: German (Deutsch) for all text fields." : "OUTPUT LANGUAGE: English.";
|
|
||||||
|
|
||||||
const prompt = `
|
|
||||||
I am a B2B Market Intelligence Architect.
|
|
||||||
|
|
||||||
--- STRATEGIC CONTEXT (Uploaded Document) ---
|
|
||||||
${contextContent}
|
|
||||||
---------------------------------------------
|
|
||||||
|
|
||||||
Reference Client URL: "${referenceUrl}"
|
|
||||||
|
|
||||||
Task: Create a "Digital Trace Strategy" to identify high-potential leads based on the Strategic Context and the Reference Client.
|
|
||||||
|
|
||||||
1. ANALYZE the uploaded context (Offer, Personas, Pain Points).
|
|
||||||
2. EXTRACT a 1-sentence summary of what is being sold ("summaryOfOffer").
|
|
||||||
3. DEFINE an Ideal Customer Profile (ICP) derived from the "Target Groups" in the context and the Reference Client.
|
|
||||||
4. **CRITICAL**: Identify 3-5 specific "Digital Signals" (Traces) visible on a company's website that indicate a match for the Pain Points/Needs defined in the context.
|
|
||||||
- Use the "Pain Points" and "Offer" from the context to derive these signals.
|
|
||||||
- Signals must be checkable via public web data.
|
|
||||||
- Example: If the context mentions "Pain: High return rates", Signal could be "Complex Return Policy in Footer" or "No automated return portal".
|
|
||||||
|
|
||||||
${langInstruction}
|
|
||||||
|
|
||||||
Output JSON format:
|
|
||||||
{
|
|
||||||
"summaryOfOffer": "Short 1-sentence summary of the product/service",
|
|
||||||
"idealCustomerProfile": "Detailed ICP based on context",
|
|
||||||
"signals": [
|
|
||||||
{
|
|
||||||
"id": "sig_1",
|
|
||||||
"name": "Short Name (e.g. 'Tech Stack')",
|
|
||||||
"description": "What specifically to look for? (e.g. 'Look for Shopify in source code')",
|
|
||||||
"targetPageKeywords": ["tech", "career", "about", "legal"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await ai.models.generateContent({
|
const response = await fetch(`${API_BASE_URL}/generate-search-strategy`, {
|
||||||
model: 'gemini-2.5-flash',
|
method: 'POST',
|
||||||
contents: prompt,
|
headers: {
|
||||||
config: { temperature: 0.4, responseMimeType: "application/json" }
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ referenceUrl, contextContent, language }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = extractJson(response.text || "{}");
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json();
|
||||||
|
throw new Error(`Backend-Fehler: ${errorData.error || response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
return {
|
return {
|
||||||
productContext: data.summaryOfOffer || "Market Analysis", // Use the AI-generated summary
|
productContext: data.summaryOfOffer || "Market Analysis",
|
||||||
idealCustomerProfile: data.idealCustomerProfile || "Companies similar to reference",
|
idealCustomerProfile: data.idealCustomerProfile || "Companies similar to reference",
|
||||||
signals: data.signals || []
|
signals: data.signals || []
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Strategy generation failed", error);
|
console.error("Strategy generation failed via API Bridge", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const identifyCompetitors = async (referenceUrl: string, targetMarket: string, language: Language): Promise<Partial<Competitor>[]> => {
|
export const identifyCompetitors = async (referenceUrl: string, targetMarket: string, language: Language): Promise<Partial<Competitor>[]> => {
|
||||||
if (!apiKey) throw new Error("API Key missing");
|
// Dieser Teil muss noch im Python-Backend implementiert werden
|
||||||
|
console.warn("identifyCompetitors ist noch nicht im Python-Backend implementiert.");
|
||||||
const langInstruction = language === 'de' ? "OUTPUT LANGUAGE: German." : "OUTPUT LANGUAGE: English.";
|
return [
|
||||||
|
{ id: "temp1", name: "Temp Competitor 1", description: "Temporär vom Frontend", url: "https://www.google.com" },
|
||||||
// UPDATED: Reduced count to 10 for speed
|
{ id: "temp2", name: "Temp Competitor 2", description: "Temporär vom Frontend", url: "https://www.bing.com" },
|
||||||
const prompt = `
|
];
|
||||||
Goal: Identify 10 DIRECT COMPETITORS or LOOKALIKES for the company found at URL: "${referenceUrl}" in "${targetMarket}".
|
|
||||||
|
|
||||||
${langInstruction}
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
1. Focus on the same business model (e.g. Retailer vs Retailer, Brand vs Brand).
|
|
||||||
2. Exclude the reference company itself.
|
|
||||||
|
|
||||||
Return JSON array: [{ "name": "...", "url": "...", "description": "..." }]
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await ai.models.generateContent({
|
|
||||||
model: 'gemini-2.5-flash',
|
|
||||||
contents: prompt,
|
|
||||||
config: { tools: [{ googleSearch: {} }], temperature: 0.4 }
|
|
||||||
});
|
|
||||||
|
|
||||||
const companies = extractJson(response.text || "[]");
|
|
||||||
return Array.isArray(companies) ? companies : [];
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Competitor search failed", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UPDATED: Dynamic Analysis based on Strategy
|
* UPDATED: Dynamic Analysis based on Strategy (muss noch im Python-Backend implementiert werden)
|
||||||
*/
|
*/
|
||||||
export const analyzeCompanyWithStrategy = async (
|
export const analyzeCompanyWithStrategy = async (
|
||||||
companyName: string,
|
companyName: string,
|
||||||
strategy: SearchStrategy,
|
strategy: SearchStrategy,
|
||||||
language: Language
|
language: Language
|
||||||
): Promise<AnalysisResult> => {
|
): Promise<AnalysisResult> => {
|
||||||
if (!apiKey) throw new Error("API Key missing");
|
// Dieser Teil muss noch im Python-Backend implementiert werden
|
||||||
|
console.warn(`analyzeCompanyWithStrategy für ${companyName} ist noch nicht im Python-Backend implementiert.`);
|
||||||
const langInstruction = language === 'de' ? "OUTPUT LANGUAGE: German." : "OUTPUT LANGUAGE: English.";
|
return {
|
||||||
|
companyName,
|
||||||
// Construct the signals prompt part
|
status: LeadStatus.UNKNOWN,
|
||||||
const signalsPrompt = strategy.signals.map(s =>
|
revenue: "?",
|
||||||
`- Signal "${s.name}" (${s.id}): Look for ${s.description}. search queries like "${companyName} ${s.targetPageKeywords.join(' ')}"`
|
employees: "?",
|
||||||
).join('\n');
|
tier: Tier.TIER_3,
|
||||||
|
dataSource: "Frontend Placeholder",
|
||||||
const prompt = `
|
dynamicAnalysis: {},
|
||||||
Perform a Deep Dive Analysis on "${companyName}".
|
recommendation: "Bitte im Backend implementieren",
|
||||||
|
processingChecks: { wiki: false, revenue: false, signalsChecked: false }
|
||||||
Context: We are selling "${strategy.productContext}".
|
};
|
||||||
|
|
||||||
${langInstruction}
|
|
||||||
|
|
||||||
--- STEP 1: FIRMOGRAPHICS (Waterfall) ---
|
|
||||||
Find Revenue and Employee count using Wikipedia -> Corp Site -> Web Search.
|
|
||||||
Classify Tier: Tier 1 (>100M), Tier 2 (>10M), Tier 3 (<10M).
|
|
||||||
|
|
||||||
--- STEP 2: DIGITAL TRACES (CUSTOM SIGNALS) ---
|
|
||||||
Investigate the following specific signals:
|
|
||||||
${signalsPrompt}
|
|
||||||
|
|
||||||
--- STEP 3: STATUS & RECOMMENDATION ---
|
|
||||||
Based on findings, determine Lead Status (Customer, Competitor, Potential).
|
|
||||||
Write a 1-sentence sales recommendation.
|
|
||||||
|
|
||||||
Output JSON:
|
|
||||||
{
|
|
||||||
"companyName": "${companyName}",
|
|
||||||
"revenue": "...",
|
|
||||||
"employees": "...",
|
|
||||||
"dataSource": "...",
|
|
||||||
"tier": "Tier 1|Tier 2|Tier 3",
|
|
||||||
"status": "Bestandskunde|Nutzt Wettbewerber|Greenfield / Potenzial|Unklar",
|
|
||||||
"recommendation": "...",
|
|
||||||
"dynamicAnalysis": {
|
|
||||||
"${strategy.signals[0]?.id || 'sig_1'}": { "value": "Short finding", "proof": "Evidence found", "sentiment": "Positive|Neutral|Negative" },
|
|
||||||
... (for all signals)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await ai.models.generateContent({
|
|
||||||
model: 'gemini-2.5-flash',
|
|
||||||
contents: prompt,
|
|
||||||
config: {
|
|
||||||
tools: [{ googleSearch: {} }],
|
|
||||||
temperature: 0.1,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const text = response.text || "{}";
|
|
||||||
const data = extractJson(text);
|
|
||||||
|
|
||||||
// Get Sources
|
|
||||||
const sources: string[] = [];
|
|
||||||
response.candidates?.[0]?.groundingMetadata?.groundingChunks?.forEach((chunk: any) => {
|
|
||||||
if (chunk.web?.uri) sources.push(chunk.web.uri);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Tier mapping logic
|
|
||||||
let mappedTier = Tier.TIER_3;
|
|
||||||
if (data.tier?.includes("Tier 1")) mappedTier = Tier.TIER_1;
|
|
||||||
else if (data.tier?.includes("Tier 2")) mappedTier = Tier.TIER_2;
|
|
||||||
|
|
||||||
// Status mapping logic
|
|
||||||
let mappedStatus = LeadStatus.UNKNOWN;
|
|
||||||
const statusStr = (data.status || "").toLowerCase();
|
|
||||||
if (statusStr.includes("bestand") || statusStr.includes("customer")) mappedStatus = LeadStatus.CUSTOMER;
|
|
||||||
else if (statusStr.includes("wettbewerb") || statusStr.includes("competitor")) mappedStatus = LeadStatus.COMPETITOR;
|
|
||||||
else if (statusStr.includes("greenfield") || statusStr.includes("poten")) mappedStatus = LeadStatus.POTENTIAL;
|
|
||||||
|
|
||||||
return {
|
|
||||||
companyName: data.companyName || companyName,
|
|
||||||
status: mappedStatus,
|
|
||||||
revenue: data.revenue || "?",
|
|
||||||
employees: data.employees || "?",
|
|
||||||
tier: mappedTier,
|
|
||||||
dataSource: data.dataSource || "Web",
|
|
||||||
dynamicAnalysis: data.dynamicAnalysis || {},
|
|
||||||
recommendation: data.recommendation || "Check manually",
|
|
||||||
sources: sources.slice(0, 3),
|
|
||||||
processingChecks: {
|
|
||||||
wiki: (data.dataSource || "").toLowerCase().includes("wiki"),
|
|
||||||
revenue: !!data.revenue,
|
|
||||||
signalsChecked: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Analysis failed for ${companyName}`, error);
|
|
||||||
return {
|
|
||||||
companyName,
|
|
||||||
status: LeadStatus.UNKNOWN,
|
|
||||||
revenue: "?",
|
|
||||||
employees: "?",
|
|
||||||
tier: Tier.TIER_3,
|
|
||||||
dataSource: "Error",
|
|
||||||
dynamicAnalysis: {},
|
|
||||||
recommendation: "Analysis Error",
|
|
||||||
processingChecks: { wiki: false, revenue: false, signalsChecked: false }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateOutreachCampaign = async (
|
export const generateOutreachCampaign = async (
|
||||||
@@ -248,92 +101,13 @@ export const generateOutreachCampaign = async (
|
|||||||
language: Language,
|
language: Language,
|
||||||
referenceUrl: string
|
referenceUrl: string
|
||||||
): Promise<EmailDraft[]> => {
|
): Promise<EmailDraft[]> => {
|
||||||
if (!apiKey) throw new Error("API Key missing");
|
// Dieser Teil muss noch im Python-Backend implementiert werden
|
||||||
|
console.warn("generateOutreachCampaign ist noch nicht im Python-Backend implementiert.");
|
||||||
// Format dynamic data for the prompt
|
return [];
|
||||||
let insights = "";
|
|
||||||
if (companyData.dynamicAnalysis) {
|
|
||||||
Object.entries(companyData.dynamicAnalysis).forEach(([key, val]) => {
|
|
||||||
// CLEAN INPUT: Do not pass internal signal IDs like sig_1 to the LLM's context if possible, or instruct it to ignore them.
|
|
||||||
// Here we just pass the observation and value.
|
|
||||||
insights += `- Observation: ${val.value} (Proof: ${val.proof})\n`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const combined = `
|
|
||||||
TARGET COMPANY: ${companyData.companyName}
|
|
||||||
SIZE: ${companyData.revenue}, ${companyData.employees}
|
|
||||||
KEY OBSERVATIONS (Web Signals):
|
|
||||||
${insights}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const langInstruction = language === 'de'
|
|
||||||
? "OUTPUT LANGUAGE: German (Deutsch). Tone: Professional, polite (Sie-form), persuasive."
|
|
||||||
: "OUTPUT LANGUAGE: English. Tone: Professional, persuasive.";
|
|
||||||
|
|
||||||
const prompt = `
|
|
||||||
You are a top-tier B2B Copywriter.
|
|
||||||
|
|
||||||
OBJECTIVE: Write 3 distinct cold email drafts to contact "${companyData.companyName}".
|
|
||||||
|
|
||||||
INPUTS:
|
|
||||||
1. STRATEGIC CONTEXT (Offer, Value Prop, Case Studies):
|
|
||||||
${knowledgeBase}
|
|
||||||
|
|
||||||
2. TARGET DATA (The recipient):
|
|
||||||
${combined}
|
|
||||||
|
|
||||||
3. REFERENCE CLIENT (Social Proof):
|
|
||||||
URL: "${referenceUrl}"
|
|
||||||
(Extract the company name from this URL to use as the Success Story / Reference)
|
|
||||||
|
|
||||||
${langInstruction}
|
|
||||||
|
|
||||||
MANDATORY RULES:
|
|
||||||
1. **NO TECHNICAL PLACEHOLDERS**: STRICTLY FORBIDDEN to use "(sig_1)", "(sig_x)", "[Name]" or similar. The text must be 100% ready to send.
|
|
||||||
|
|
||||||
2. **MANDATORY DYNAMIC SOCIAL PROOF**: You MUST include a specific paragraph referencing the "Reference Client" provided above, but **ADAPTED TO THE PERSONA** of the email.
|
|
||||||
- **CRITICAL**: Do NOT use the same generic KPI for all emails.
|
|
||||||
- **LOGIC**: Look at the "Strategic Context" for KPIs.
|
|
||||||
- If the persona is **Operational** (COO, Ops Manager): Focus on **Efficiency, Speed, Automation** (e.g. "Did you know [Reference] saved 20% time...?").
|
|
||||||
- If the persona is **HR / People**: Focus on **Staff Retention, Workload Relief** (e.g. "Did you know [Reference] relieved their staff by X hours...?").
|
|
||||||
- If the persona is **Strategic / General**: Focus on **Cost, Revenue, Innovation** (e.g. "Did you know [Reference] increased ROI by...?").
|
|
||||||
|
|
||||||
- Structure (German): "Übrigens: Wussten Sie, dass [Reference Client Name] [Persona-relevant KPI]...?"
|
|
||||||
- Structure (English): "By the way: Did you know that [Reference Client Name] [Persona-relevant KPI]...?"
|
|
||||||
|
|
||||||
3. **HYPER-PERSONALIZATION**: Use the "Key Observations" (Web Signals) to connect the problem to the solution. Don't just list them.
|
|
||||||
|
|
||||||
Output JSON format:
|
|
||||||
[{ "persona": "Target Role (e.g. COO)", "subject": "...", "body": "...", "keyPoints": ["Used observation X", "Mentioned Reference Client (Persona adjusted)"] }]
|
|
||||||
`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await ai.models.generateContent({
|
|
||||||
model: 'gemini-2.5-flash',
|
|
||||||
contents: prompt,
|
|
||||||
config: { temperature: 0.7, responseMimeType: "application/json" }
|
|
||||||
});
|
|
||||||
|
|
||||||
const drafts = extractJson(response.text || "[]");
|
|
||||||
return Array.isArray(drafts) ? drafts : [];
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Outreach generation failed", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const translateEmailDrafts = async (drafts: EmailDraft[], targetLanguage: Language): Promise<EmailDraft[]> => {
|
export const translateEmailDrafts = async (drafts: EmailDraft[], targetLanguage: Language): Promise<EmailDraft[]> => {
|
||||||
if (!apiKey) throw new Error("API Key missing");
|
// Dieser Teil muss noch im Python-Backend oder direkt im Frontend implementiert werden
|
||||||
const langName = targetLanguage === 'de' ? 'German' : targetLanguage === 'fr' ? 'French' : 'English';
|
console.warn("translateEmailDrafts ist noch nicht im Python-Backend implementiert.");
|
||||||
const prompt = `Translate this JSON array to ${langName}. Keep JSON structure. Input: ${JSON.stringify(drafts)}`;
|
return drafts;
|
||||||
|
}
|
||||||
try {
|
|
||||||
const response = await ai.models.generateContent({
|
|
||||||
model: 'gemini-2.5-flash',
|
|
||||||
contents: prompt,
|
|
||||||
config: { responseMimeType: "application/json" }
|
|
||||||
});
|
|
||||||
return extractJson(response.text || "[]");
|
|
||||||
} catch (e) { return drafts; }
|
|
||||||
}
|
|
||||||
@@ -85,4 +85,34 @@ Die Logik aus `geminiService.ts` wird in Python-Funktionen innerhalb von `market
|
|||||||
2. **Implementierung (Node.js):** Erstellen der `server.js` als API-Brücke im React-Projekt.
|
2. **Implementierung (Node.js):** Erstellen der `server.js` als API-Brücke im React-Projekt.
|
||||||
3. **Anpassung (React):** Modifizieren der `geminiService.ts`, um die Aufrufe an die lokale API-Brücke (`/api/...`) statt direkt an die Gemini-API zu senden.
|
3. **Anpassung (React):** Modifizieren der `geminiService.ts`, um die Aufrufe an die lokale API-Brücke (`/api/...`) statt direkt an die Gemini-API zu senden.
|
||||||
4. **Containerisierung (Docker):** Erstellen eines `Dockerfile`, das die Python- und Node.js-Umgebung aufsetzt und den Service startet.
|
4. **Containerisierung (Docker):** Erstellen eines `Dockerfile`, das die Python- und Node.js-Umgebung aufsetzt und den Service startet.
|
||||||
5. **Testen:** Umfassendes Testen des gesamten End-to-End-Flows.
|
5. **Testen:** Umfassendes Testen des gesamten End-to-End-Flows.
|
||||||
|
|
||||||
|
## 5. Aktuelle Probleme und Debugging-Protokoll (Stand: 2025-12-21)
|
||||||
|
|
||||||
|
Wir stecken derzeit in einem hartnäckigen `ImportError: cannot import name 'cygrpc' from 'grpc._cython'` Fehler fest, der beim Starten des Python-Skripts (`market_intel_orchestrator.py`) auftritt.
|
||||||
|
|
||||||
|
**Bisher unternommene Schritte zur Problemlösung:**
|
||||||
|
|
||||||
|
1. **Virtuelle Umgebung (.venv) erstellt:** Um Paketkonflikte zu isolieren.
|
||||||
|
2. **`python3.11-venv` installiert:** Um `venv` unter Debian/Ubuntu zu ermöglichen.
|
||||||
|
3. **`requirements.txt` bereinigt und Paketversionen gepinnt:**
|
||||||
|
* `requests==2.28.2` und `urllib3==1.26.18` (behob `TypeError: 'type' object is not subscriptable`).
|
||||||
|
* `typing-extensions==4.5.0` (behob `AttributeError: module 'typing' has no attribute '_SpecialGenericAlias'`).
|
||||||
|
* `google-generativeai==0.4.0` (gepinnt, um Kompatibilität mit älteren `google-api-core` und `grpcio` zu erzwingen).
|
||||||
|
* `grpcio==1.54.2` und `google-api-core==2.11.1` (gepinnt, sollte `cygrpc` beheben, hat es aber nicht).
|
||||||
|
4. **`gemini_api_key.txt` geprüft:** Sichergestellt, dass nur der reine API-Schlüssel enthalten ist.
|
||||||
|
5. **Gemini-Modell gewechselt:** Von `gemini-1.5-flash` zu `gemini-pro`, dann `responseMimeType` zu `text/plain` geändert (dies war eine Umgehung zur Diagnose, der `404 Not Found`-Fehler trat weiterhin auf, was auf ein tieferes Autorisierungs- oder Kompilierungsproblem hindeutet).
|
||||||
|
6. **Node.js API-Brücke (`server.cjs`) angepasst:** Sichergestellt, dass der Python-Subprozess mit dem korrekten Venv-Interpreter und der `PYTHONPATH` gestartet wird (behob `ModuleNotFoundError: No module named 'requests'`).
|
||||||
|
7. **`grpcio` deinstalliert und Build-Tools installiert:** `build-essential` und `python3-dev` wurden installiert, um eine Kompilierung von `grpcio` aus dem Quellcode zu ermöglichen.
|
||||||
|
|
||||||
|
**Aktuelles Problem:**
|
||||||
|
|
||||||
|
Der Fehler `ImportError: cannot import name 'cygrpc' from 'grpc._cython'` bleibt bestehen, selbst nach dem Versuch, `grpcio` neu zu kompilieren (der Kompilierungsschritt selbst konnte nicht vollständig durchgeführt werden).
|
||||||
|
|
||||||
|
Dieser Fehler ist ein Indikator dafür, dass die **vor-kompilierten `grpcio`-Wheels** nicht mit der spezifischen Systemumgebung (Python-Version, Betriebssystem, installierte Bibliotheken) kompatibel sind, oder dass die **Kompilierung aus dem Quellcode fehlschlägt**, weil immer noch eine Abhängigkeit oder ein Build-Tool auf Systemebene fehlt oder inkompatibel ist.
|
||||||
|
|
||||||
|
**Mögliche nächste Schritte (manuell durch den Benutzer):**
|
||||||
|
|
||||||
|
- **Erneuter Versuch der `grpcio`-Kompilierung:** Führen Sie den Befehl `pip install --no-binary :all: grpcio==1.54.2` erneut aus. Beobachten Sie die Ausgabe genau auf Kompilierungsfehler. Der Prozess kann sehr lange dauern.
|
||||||
|
- **Upgrade/Downgrade der Python-Version:** Das Problem könnte mit Python 3.11 spezifisch sein. Ein Versuch mit Python 3.10 oder 3.9 könnte helfen, ist aber ein größerer Eingriff ins System.
|
||||||
|
- **Docker-Ansatz vorziehen:** Die sauberste und reproduzierbarste Lösung wäre, den gesamten Backend-Stack in einem Docker-Container zu betreiben. Innerhalb eines Dockerfiles können wir die Umgebung exakt steuern und die Installation der Abhängigkeiten von Grund auf neu aufbauen, was solche `cygrpc`-Probleme oft umgeht.
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ xgboost==1.7.6
|
|||||||
google-api-python-client
|
google-api-python-client
|
||||||
google-auth-httplib2
|
google-auth-httplib2
|
||||||
google-auth-oauthlib
|
google-auth-oauthlib
|
||||||
# Füge hier alle anderen Pakete hinzu, die deine Helfer-Skripte eventuell noch benötigen
|
|
||||||
gspread
|
gspread
|
||||||
oauth2client
|
oauth2client
|
||||||
requests
|
requests==2.28.2
|
||||||
|
urllib3==1.26.18
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
lxml
|
lxml
|
||||||
wikipedia
|
wikipedia
|
||||||
@@ -25,4 +25,8 @@ python-docx
|
|||||||
PyYAML
|
PyYAML
|
||||||
openpyxl
|
openpyxl
|
||||||
Flask
|
Flask
|
||||||
pyngrok
|
pyngrok
|
||||||
|
google-generativeai==0.4.0
|
||||||
|
typing-extensions==4.5.0
|
||||||
|
grpcio==1.54.2
|
||||||
|
google-api-core==2.11.1
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user