n8n-nodes-berget-mk 0.4.13 → 0.4.15

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/README.md CHANGED
@@ -4,10 +4,10 @@ n8n community nodes for [Berget AI](https://berget.ai), packaged as a single ins
4
4
 
5
5
  Four nodes:
6
6
 
7
- - **Berget AI** — multi-resource action node for one-shot calls: **Chat** (completions, classification), **Image Analysis** (vision models), **Rerank** (document reranking), and **Speech to Text** (Swedish-tuned KB-Whisper). Can also be exposed as a tool to an AI Agent. (OCR is temporarily hidden — see [CHANGELOG.md](CHANGELOG.md) for `0.4.4` for details.)
7
+ - **Berget AI** — multi-resource action node for one-shot calls. Resources: **Chat** (completions, classification, JSON Schema structured output), **Image Analysis** (vision-capable models), **Rerank** (document reranking), and **Speech to Text** (Swedish-tuned KB-Whisper, with optional diarization and word-level alignment). Can also be exposed as a tool to an AI Agent. (OCR is temporarily hidden — see [CHANGELOG.md](CHANGELOG.md) for `0.4.4` for details.)
8
8
  - **Berget AI Chat Model** — sub-node that plugs into n8n's built-in **AI Agent**, **Basic LLM Chain**, and other LangChain-based nodes. Exposes `reasoning_effort` and the full standard LLM parameter set.
9
9
  - **Berget AI Embeddings Model** — sub-node that plugs into n8n's **Vector Store** nodes (Supabase, Qdrant, Pinecone, PGVector, etc.) and **Question and Answer Chain**.
10
- - **Berget AI Reranker** — sub-node that plugs into Vector Store retrievers to reorder candidate documents by relevance before handing them to the chain or agent.
10
+ - **Berget AI Reranker** — sub-node that plugs into Vector Store retrievers via the `AiReranker` connection, reordering candidates by relevance before they reach the agent or chain.
11
11
 
12
12
  > ⚠️ **Experimental — actively developed.** This package is pre-1.0 and may break between minor releases. Pin a specific version in production workflows until `1.0.0`. See [CHANGELOG.md](CHANGELOG.md) for breaking changes.
13
13
 
@@ -27,21 +27,35 @@ Then add a **Berget AI API** credential with your API key from [berget.ai](https
27
27
 
28
28
  1. Drop **Berget AI** onto the canvas, pick Resource = **Chat**, select a model, add a user message. Execute.
29
29
 
30
+ For classification or structured extraction tasks, set **Options → Response Format = JSON Schema** and provide a schema. The model is forced to return parseable JSON conforming to your shape — no regex scraping of free-form text.
31
+
30
32
  ### Agent with tools and memory
31
33
 
32
34
  1. Add n8n's built-in **AI Agent**.
33
35
  2. Add **Berget AI Chat Model** and connect it to the Agent's Chat Model socket.
34
36
  3. Add Memory and Tool sub-nodes as needed — they work with Berget as the underlying LLM.
35
37
 
36
- ### RAG / vector search
38
+ ### RAG with retrieval and reranking
37
39
 
38
- 1. Add a Vector Store node (Supabase, Qdrant, etc.) or a Question and Answer Chain.
40
+ 1. Add a Vector Store node (Qdrant, Supabase, etc.) or a Question and Answer Chain.
39
41
  2. Add **Berget AI Embeddings Model** and connect it to the Embedding socket.
40
- 3. Index documents or query as usual.
42
+ 3. Add **Berget AI Reranker** and connect it to the EmbeddingReranker socket — Vector Store will then retrieve a wider candidate set, the reranker reorders them by relevance, and only the best survive into the answer.
43
+ 4. Index documents or query as usual.
44
+
45
+ ### Image analysis
46
+
47
+ 1. Drop **Berget AI** onto the canvas, pick Resource = **Image Analysis**.
48
+ 2. Pick a vision-capable model (the dropdown is filtered automatically).
49
+ 3. Choose Input Type = **Binary File** (default — works with Form Trigger uploads, HTTP Request responses, etc.) or **Image URL**, and provide a **Text Input** prompt like `"Describe what you see"`.
50
+ 4. Execute.
41
51
 
42
- ### Swedish speech transcription
52
+ ### Swedish speech transcription with speakers
43
53
 
44
- 1. Drop **Berget AI** onto the canvas, pick Resource = **Speech to Text**, pick a model (defaults to `KB-Whisper-Large`), and point at an audio file.
54
+ 1. Drop **Berget AI** onto the canvas, pick Resource = **Speech to Text**.
55
+ 2. Provide the binary input data from a Form Trigger or HTTP Request.
56
+ 3. Optional: enable **Options → Diarize (Speaker Identification)** — the response will include a `speaker_transcript` field formatted as readable per-speaker paragraphs (`SPEAKER_00:\n...\n\nSPEAKER_01:\n...`), alongside the raw segment-level timestamps and word-level data.
57
+ 4. Optional: enable **Word-Level Alignment** for per-word timestamps useful in subtitle generation.
58
+ 5. Optional: add **Hotwords** (comma-separated) for proper nouns and domain vocabulary.
45
59
 
46
60
  ## Changelog
47
61
 
@@ -84,7 +84,7 @@ exports.chatProperties = [
84
84
  { name: 'JSON Schema', value: 'json_schema' },
85
85
  ],
86
86
  default: 'text',
87
- description: 'Force the model to return a specific response format. "JSON Object" tells the model to return any valid JSON. "JSON Schema" enforces a specific JSON schema you provide — set the schema with the JSON Schema fields below.',
87
+ description: 'Force the model to return a specific response format. "JSON Object" tells the model to return any valid JSON. "JSON Schema" enforces a specific JSON schema you provide — set the schema with the JSON Schema fields below. When either is selected, the parsed JSON is also exposed as a top-level "output" field on the node\'s output so downstream nodes (IF, Set, etc.) can reference its properties directly.',
88
88
  },
89
89
  {
90
90
  displayName: 'JSON Schema Name',
@@ -131,6 +131,7 @@ exports.chatProperties = [
131
131
  },
132
132
  ];
133
133
  async function executeChat(context, itemIndex) {
134
+ var _a, _b;
134
135
  const credentials = await context.getCredentials('bergetAiApi');
135
136
  const model = context.getNodeParameter('chatModel', itemIndex);
136
137
  const messages = context.getNodeParameter('chatMessages.values', itemIndex, []);
@@ -173,5 +174,22 @@ async function executeChat(context, itemIndex) {
173
174
  if (status !== 200) {
174
175
  throw new n8n_workflow_1.NodeOperationError(context.getNode(), (0, shared_1.formatBergetError)('chat', status, data), { itemIndex });
175
176
  }
176
- return data;
177
+ const result = data;
178
+ if (responseFormat === 'json_object' || responseFormat === 'json_schema') {
179
+ const choices = result.choices;
180
+ const rawContent = (_b = (_a = choices === null || choices === void 0 ? void 0 : choices[0]) === null || _a === void 0 ? void 0 : _a.message) === null || _b === void 0 ? void 0 : _b.content;
181
+ if (typeof rawContent === 'string' && rawContent.trim().length > 0) {
182
+ try {
183
+ const parsed = JSON.parse(rawContent);
184
+ if (parsed && typeof parsed === 'object') {
185
+ result.output = parsed;
186
+ }
187
+ }
188
+ catch {
189
+ // Model returned non-JSON despite response_format being set.
190
+ // Leave output absent; raw string stays in choices[0].message.content.
191
+ }
192
+ }
193
+ }
194
+ return result;
177
195
  }
@@ -199,18 +199,42 @@ async function executeSpeech(context, itemIndex) {
199
199
  // segments and word timestamps. The raw segments/words/timestamps are
200
200
  // still preserved on the result object so power users can drill into
201
201
  // them when needed.
202
- if (options.diarize &&
203
- data &&
204
- typeof data === 'object' &&
205
- Array.isArray(data.segments)) {
206
- const segments = data.segments;
207
- const transcript = buildSpeakerTranscript(segments);
208
- if (transcript) {
209
- data.speaker_transcript = transcript;
202
+ if (options.diarize && data && typeof data === 'object') {
203
+ const segments = extractSegments(data);
204
+ if (segments) {
205
+ const transcript = buildSpeakerTranscript(segments);
206
+ if (transcript) {
207
+ data.speaker_transcript = transcript;
208
+ }
210
209
  }
211
210
  }
212
211
  return data;
213
212
  }
213
+ /**
214
+ * Pull the segments array out of Berget's transcription response. Berget has
215
+ * been observed to return the segments in two different shapes:
216
+ *
217
+ * Shape A (flat): { segments: [...], language, text }
218
+ * Shape B (nested): { segments: { segments: [...], ... }, language, text }
219
+ *
220
+ * Shape B is what the API returns for verbose_json with diarize=true (as of
221
+ * 2026-04). We check both so the speaker_transcript builder works regardless
222
+ * of which shape we get, and so future API changes that flatten or re-nest
223
+ * don't silently break the output.
224
+ */
225
+ function extractSegments(data) {
226
+ const top = data.segments;
227
+ if (Array.isArray(top)) {
228
+ return top;
229
+ }
230
+ if (top && typeof top === 'object') {
231
+ const inner = top.segments;
232
+ if (Array.isArray(inner)) {
233
+ return inner;
234
+ }
235
+ }
236
+ return undefined;
237
+ }
214
238
  /**
215
239
  * Build a "SPEAKER_00:\n...text...\n\nSPEAKER_01:\n..." style transcript
216
240
  * by walking the segments array, grouping consecutive segments that share
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-berget-mk",
3
- "version": "0.4.13",
3
+ "version": "0.4.15",
4
4
  "description": "n8n community node for Berget AI. Multi-resource action node (chat, OCR, rerank, speech-to-text) plus Chat Model and Embeddings Model sub-nodes that plug into n8n's built-in AI Agent and Vector Store nodes.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",