intelwatch 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/export.pdf +0 -0
- package/package.json +1 -1
- package/profile-480254275.pdf +0 -0
- package/profile-775726417.pdf +0 -0
- package/profile-794598813.pdf +0 -0
- package/src/ai/client.js +39 -1
- package/src/commands/profile.js +17 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [1.3.2] - 2026-03-21
|
|
2
|
+
### Added
|
|
3
|
+
- **Google Gemini Provider**: Full support for Gemini models via Google API (`GEMINI_API_KEY` or `GOOGLE_API_KEY`) for Due Diligence AI analysis
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **BODACC Limit**: Reduced BODACC publication injection from 50 to 5 entries for AI context to drastically reduce token usage and prevent truncation
|
|
7
|
+
- **M&A Output**: Reduced AI verbosity on health scores by enforcing strict bullet points constraints
|
|
8
|
+
- **MaxTokens Override**: Removed hardcoded `maxTokens=1000` default and bumped to `8192` to allow full M&A history to generate without arbitrary cutoff
|
|
9
|
+
- **Group Structure Render**: Fixed logic that accidentally injected Private Equity shareholders (like BPIFrance) into the subsidiaries array
|
|
10
|
+
|
|
1
11
|
# CHANGELOG - v1.2 Draft
|
|
2
12
|
|
|
3
13
|
## Version 1.2.0 (en développement)
|
|
@@ -182,3 +192,8 @@ All notable changes to this project will be documented in this file.
|
|
|
182
192
|
- M&A History PDF generation bug: Stopped truncating AI timeline events, full timeline is now preserved.
|
|
183
193
|
- Group Structure Classification: Prevented PE Funds (BPIFrance, IK Partners, etc.) from being improperly categorized as operational subsidiaries in the AI due diligence report.
|
|
184
194
|
- Fixed `pdfData` passthrough bug that caused empty PDF exports.
|
|
195
|
+
|
|
196
|
+
### v1.3.1 (2026-03-21)
|
|
197
|
+
- **Bug Fix**: Fixed an issue where PDF exports could contain empty Group Structure and M&A History sections leading to ugly page breaks.
|
|
198
|
+
- **Bug Fix**: Preserved AI-generated subsidiaries when registry fallback has missing revenue data.
|
|
199
|
+
- **Bug Fix**: Fixed PDF context scope throwing an undefined error when exporting via the `--export pdf` flag instead of the legacy `--format` option.
|
package/export.pdf
ADDED
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/ai/client.js
CHANGED
|
@@ -5,9 +5,13 @@ import { loadConfig } from '../config.js';
|
|
|
5
5
|
* Returns null if no key is configured.
|
|
6
6
|
*/
|
|
7
7
|
export function getAIConfig() {
|
|
8
|
+
const envGoogle = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
|
|
8
9
|
const envOpenAI = process.env.OPENAI_API_KEY;
|
|
9
10
|
const envAnthropic = process.env.ANTHROPIC_API_KEY;
|
|
10
11
|
|
|
12
|
+
if (envGoogle) {
|
|
13
|
+
return { provider: 'google', apiKey: envGoogle, model: 'gemini-2.5-flash' };
|
|
14
|
+
}
|
|
11
15
|
if (envOpenAI) {
|
|
12
16
|
return { provider: 'openai', apiKey: envOpenAI, model: 'gpt-4o-mini' };
|
|
13
17
|
}
|
|
@@ -47,7 +51,7 @@ export async function callAI(systemPrompt, userPrompt, options = {}) {
|
|
|
47
51
|
const aiConfig = getAIConfig();
|
|
48
52
|
if (!aiConfig) {
|
|
49
53
|
throw new Error(
|
|
50
|
-
'No AI API key configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY, ' +
|
|
54
|
+
'No AI API key configured. Set GEMINI_API_KEY, OPENAI_API_KEY or ANTHROPIC_API_KEY, ' +
|
|
51
55
|
'or add ai.api_key to ~/.intelwatch/config.yml'
|
|
52
56
|
);
|
|
53
57
|
}
|
|
@@ -55,6 +59,9 @@ export async function callAI(systemPrompt, userPrompt, options = {}) {
|
|
|
55
59
|
const { provider, apiKey, model } = aiConfig;
|
|
56
60
|
const maxTokens = options.maxTokens || 1000;
|
|
57
61
|
|
|
62
|
+
if (provider === 'google') {
|
|
63
|
+
return callGoogle(apiKey, model, systemPrompt, userPrompt, maxTokens);
|
|
64
|
+
}
|
|
58
65
|
if (provider === 'anthropic') {
|
|
59
66
|
return callAnthropic(apiKey, model, systemPrompt, userPrompt, maxTokens);
|
|
60
67
|
}
|
|
@@ -128,3 +135,34 @@ export function estimateCost(inputChars, outputChars, provider = 'openai') {
|
|
|
128
135
|
const cost = inputTokens * rates.in + outputTokens * rates.out;
|
|
129
136
|
return { inputTokens, outputTokens, cost: cost.toFixed(5) };
|
|
130
137
|
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async function callGoogle(apiKey, model, systemPrompt, userPrompt, maxTokens) {
|
|
141
|
+
// Use v1beta for Gemini 2.5
|
|
142
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
143
|
+
const res = await fetch(url, {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: {
|
|
146
|
+
'Content-Type': 'application/json',
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
150
|
+
contents: [{ role: 'user', parts: [{ text: userPrompt }] }],
|
|
151
|
+
generationConfig: {
|
|
152
|
+
maxOutputTokens: maxTokens,
|
|
153
|
+
temperature: 0.2
|
|
154
|
+
}
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
const body = await res.text();
|
|
160
|
+
throw new Error(`Google API ${res.status}: ${body.slice(0, 200)}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
if (!data.candidates || !data.candidates[0].content) {
|
|
165
|
+
throw new Error('Invalid Google API response');
|
|
166
|
+
}
|
|
167
|
+
return data.candidates[0].content.parts[0].text.trim();
|
|
168
|
+
}
|
package/src/commands/profile.js
CHANGED
|
@@ -799,6 +799,7 @@ OBLIGATOIRE :
|
|
|
799
799
|
- ${getPrompt('strengthsWeaknessesRules')}
|
|
800
800
|
- Minimum 5 concurrents de taille comparable (CA consolidé similaire, même code NAF ${identity.nafCode || ''})
|
|
801
801
|
- Le score de santé doit être basé sur les finances CONSOLIDÉES si disponibles
|
|
802
|
+
- BE EXTREMELY CONCISE. Use bullet points and short sentences. Max 30 words per field.
|
|
802
803
|
- Ne mentionne JAMAIS que la holding a peu d'employés comme faiblesse — c'est normal pour une holding, les employés sont dans les filiales
|
|
803
804
|
- maHistory: The PRE-BUILT M&A TIMELINE above contains ALL entries with AUTHORITATIVE dates and types.
|
|
804
805
|
RULES:
|
|
@@ -817,7 +818,7 @@ OBLIGATOIRE :
|
|
|
817
818
|
- aiComment: 3-4 sentences. Compare deposited (62M€ 2024) vs announced/projected. Be specific. If multiple revenue targets exist (e.g. 100M€ and 300M€), explain both.
|
|
818
819
|
- aiComment: 3-4 sentences comparing deposited vs announced/projected, discussing growth sustainability and outlook`;
|
|
819
820
|
|
|
820
|
-
const raw = await callAI(systemPrompt, userPrompt, { maxTokens:
|
|
821
|
+
const raw = await callAI(systemPrompt, userPrompt, { maxTokens: 8192 });
|
|
821
822
|
aiAnalysis = extractAIJSON(raw);
|
|
822
823
|
|
|
823
824
|
// M&A History: Merging code-built events with AI events instead of overwriting
|
|
@@ -944,8 +945,9 @@ OBLIGATOIRE :
|
|
|
944
945
|
}
|
|
945
946
|
}
|
|
946
947
|
|
|
948
|
+
let pdfData = null;
|
|
947
949
|
// ── PDF export ──────────────────────────────────────────────────────────────
|
|
948
|
-
if (options.format === 'pdf') {
|
|
950
|
+
if (options.format === 'pdf' || options.export === 'pdf') {
|
|
949
951
|
const outputPath = options.output || `profile-${siren}.pdf`;
|
|
950
952
|
const fmtEuro = (n) => {
|
|
951
953
|
if (n == null) return '—';
|
|
@@ -964,22 +966,24 @@ OBLIGATOIRE :
|
|
|
964
966
|
});
|
|
965
967
|
}
|
|
966
968
|
|
|
967
|
-
|
|
969
|
+
pdfData = {
|
|
968
970
|
aiSummary: aiAnalysis?.executiveSummary || null,
|
|
969
971
|
groupStructure: (() => {
|
|
970
972
|
const gs = aiAnalysis?.groupStructure || {};
|
|
971
|
-
//
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
973
|
+
// Combine AI subsidiaries with real data
|
|
974
|
+
const pappersSubs = (subsidiariesData || [])
|
|
975
|
+
.filter(s => s.ca && s.ca > 0)
|
|
976
|
+
.sort((a, b) => (b.ca || 0) - (a.ca || 0))
|
|
977
|
+
.slice(0, 7)
|
|
978
|
+
.map(s => ({ entity: s.name, revenue: `${(s.ca / 1e6).toFixed(1)} M€${s.annee ? ' (' + s.annee + ')' : ''}` }));
|
|
979
|
+
|
|
980
|
+
if (pappersSubs.length > 0) {
|
|
981
|
+
gs.subsidiaries = pappersSubs;
|
|
978
982
|
}
|
|
979
983
|
return gs;
|
|
980
984
|
})(),
|
|
981
985
|
aiCompetitors: aiAnalysis?.competitors || [],
|
|
982
|
-
maHistory: aiAnalysis?.maHistory || [],
|
|
986
|
+
maHistory: (aiAnalysis?.maHistory?.length ? aiAnalysis.maHistory : codeBuiltMaHistory) || [],
|
|
983
987
|
riskAssessment: aiAnalysis?.riskAssessment || null,
|
|
984
988
|
healthScore: aiAnalysis?.healthScore || null,
|
|
985
989
|
growthAnalysis: (() => {
|
|
@@ -1210,7 +1214,7 @@ OBLIGATOIRE :
|
|
|
1210
1214
|
siren: r.siren,
|
|
1211
1215
|
})),
|
|
1212
1216
|
// Etablissements
|
|
1213
|
-
etablissements: (etablissements || []).map(e => ({
|
|
1217
|
+
etablissements: (etablissements || []).filter(e => e.actif !== false).map(e => ({
|
|
1214
1218
|
siret: e.siret,
|
|
1215
1219
|
type: e.type,
|
|
1216
1220
|
address: e.adresse,
|
|
@@ -1254,7 +1258,7 @@ OBLIGATOIRE :
|
|
|
1254
1258
|
bodacc: (bodacc || []).slice(0, 15).map(b => ({
|
|
1255
1259
|
date: b.date || '—',
|
|
1256
1260
|
type: b.type || '—',
|
|
1257
|
-
description: b.description || '',
|
|
1261
|
+
description: (b.description && b.description.length > 140) ? b.description.substring(0, 140) + '...' : (b.description || ''),
|
|
1258
1262
|
url: b.url || null,
|
|
1259
1263
|
})),
|
|
1260
1264
|
// Procédures collectives
|