@eeacms/volto-eea-chatbot 1.0.12 → 1.0.13
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 +12 -0
- package/package.json +1 -1
- package/src/halloumi/filtering.js +12 -6
- package/src/halloumi/generative.js +12 -7
- package/src/halloumi/middleware.js +1 -0
- package/src/middleware.js +3 -0
- package/src/middleware.test.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [1.0.13](https://github.com/eea/volto-eea-chatbot/compare/1.0.12...1.0.13) - 4 March 2026
|
|
8
|
+
|
|
9
|
+
#### :house: Internal changes
|
|
10
|
+
|
|
11
|
+
- style: Automated code fix [eea-jenkins - [`8a8c3c4`](https://github.com/eea/volto-eea-chatbot/commit/8a8c3c4172a4f669661378cf1b5a3569d85609e6)]
|
|
12
|
+
- chore: [JENKINSFILE] add package version in sonarqube [valentinab25 - [`535d986`](https://github.com/eea/volto-eea-chatbot/commit/535d986b7adc77743a668bc4ac63f835eef58df3)]
|
|
13
|
+
|
|
14
|
+
#### :hammer_and_wrench: Others
|
|
15
|
+
|
|
16
|
+
- update [Miu Razvan - [`04e6c3f`](https://github.com/eea/volto-eea-chatbot/commit/04e6c3f776bb9920b89a014cc97f8e5dbb284a48)]
|
|
17
|
+
- update [Miu Razvan - [`f9e9beb`](https://github.com/eea/volto-eea-chatbot/commit/f9e9beb0676b215a50226a0db8c5be7c540f26ff)]
|
|
18
|
+
- Forward client ip to onyx/llmgw requests, ref #298095 [Miu Razvan - [`e90a672`](https://github.com/eea/volto-eea-chatbot/commit/e90a672273f2220d2cced4ad53c4b3ed3f295691)]
|
|
7
19
|
### [1.0.12](https://github.com/eea/volto-eea-chatbot/compare/1.0.11...1.0.12) - 23 February 2026
|
|
8
20
|
|
|
9
21
|
#### :house: Internal changes
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ const filterModel = {
|
|
|
13
13
|
apiKey: LLMGW_API_KEY,
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export async function callLLM(apiUrl, apiKey, requestBody) {
|
|
16
|
+
export async function callLLM(apiUrl, apiKey, requestBody, { ip } = {}) {
|
|
17
17
|
const headers = {
|
|
18
18
|
'Content-Type': 'application/json',
|
|
19
19
|
accept: 'application/json',
|
|
@@ -21,6 +21,9 @@ export async function callLLM(apiUrl, apiKey, requestBody) {
|
|
|
21
21
|
if (apiKey) {
|
|
22
22
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
23
23
|
}
|
|
24
|
+
if (ip) {
|
|
25
|
+
headers['X-Forwarded-For'] = ip;
|
|
26
|
+
}
|
|
24
27
|
|
|
25
28
|
const response = await fetch(apiUrl, {
|
|
26
29
|
method: 'POST',
|
|
@@ -92,24 +95,26 @@ export function parseExcludeIndices(content, maxIndex) {
|
|
|
92
95
|
return excludeIndices;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
async function callFilterModel(prompt) {
|
|
98
|
+
async function callFilterModel(prompt, { ip } = {}) {
|
|
96
99
|
const data = {
|
|
97
100
|
messages: [{ role: 'user', content: prompt }],
|
|
98
101
|
temperature: 0.0,
|
|
99
102
|
model: filterModel.name,
|
|
100
103
|
};
|
|
101
|
-
const jsonData = await callLLM(filterModel.apiUrl, filterModel.apiKey, data
|
|
104
|
+
const jsonData = await callLLM(filterModel.apiUrl, filterModel.apiKey, data, {
|
|
105
|
+
ip,
|
|
106
|
+
});
|
|
102
107
|
return jsonData.choices?.[0]?.message?.content || '';
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
export async function excludeClaimSentences(sentences) {
|
|
110
|
+
export async function excludeClaimSentences(sentences, { ip } = {}) {
|
|
106
111
|
if (sentences.length === 0) {
|
|
107
112
|
return new Set();
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
try {
|
|
111
116
|
const prompt = buildClaimFilterPrompt(sentences);
|
|
112
|
-
const content = await callFilterModel(prompt);
|
|
117
|
+
const content = await callFilterModel(prompt, { ip });
|
|
113
118
|
const excludedIndices = parseExcludeIndices(content, sentences.length);
|
|
114
119
|
log('Claim filter response', excludedIndices.size);
|
|
115
120
|
return excludedIndices;
|
|
@@ -122,6 +127,7 @@ export async function excludeClaimSentences(sentences) {
|
|
|
122
127
|
export async function excludeContextSentences(
|
|
123
128
|
contextSentences,
|
|
124
129
|
claimSentences,
|
|
130
|
+
{ ip } = {},
|
|
125
131
|
) {
|
|
126
132
|
if (contextSentences.length <= MIN_CONTEXT_SENTENCES_FOR_FILTERING) {
|
|
127
133
|
return new Set();
|
|
@@ -129,7 +135,7 @@ export async function excludeContextSentences(
|
|
|
129
135
|
|
|
130
136
|
try {
|
|
131
137
|
const prompt = buildContextFilterPrompt(contextSentences, claimSentences);
|
|
132
|
-
const content = await callFilterModel(prompt);
|
|
138
|
+
const content = await callFilterModel(prompt, { ip });
|
|
133
139
|
const excludedIndices = parseExcludeIndices(
|
|
134
140
|
content,
|
|
135
141
|
contextSentences.length,
|
|
@@ -52,7 +52,12 @@ function mergeChunkClaims(chunkResults) {
|
|
|
52
52
|
return Array.from(claimMap.values());
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export async function getVerifyClaimResponse(
|
|
55
|
+
export async function getVerifyClaimResponse(
|
|
56
|
+
model,
|
|
57
|
+
sources,
|
|
58
|
+
answer,
|
|
59
|
+
{ ip } = {},
|
|
60
|
+
) {
|
|
56
61
|
const emptyResponse = {
|
|
57
62
|
claims: [],
|
|
58
63
|
segments: {},
|
|
@@ -67,7 +72,7 @@ export async function getVerifyClaimResponse(model, sources, answer) {
|
|
|
67
72
|
|
|
68
73
|
// Filter claims and context in parallel
|
|
69
74
|
const [excludeResponseIndices] = await Promise.all([
|
|
70
|
-
excludeClaimSentences(responseSentences),
|
|
75
|
+
excludeClaimSentences(responseSentences, { ip }),
|
|
71
76
|
]);
|
|
72
77
|
|
|
73
78
|
const contextSentences = [];
|
|
@@ -109,7 +114,7 @@ export async function getVerifyClaimResponse(model, sources, answer) {
|
|
|
109
114
|
const chunkResults = await Promise.all(
|
|
110
115
|
prompts.map((chunkPrompt, i) => {
|
|
111
116
|
log(`Chunk ${i + 1} request`);
|
|
112
|
-
return halloumiGenerativeAPI(model, chunkPrompt);
|
|
117
|
+
return halloumiGenerativeAPI(model, chunkPrompt, { ip });
|
|
113
118
|
}),
|
|
114
119
|
);
|
|
115
120
|
|
|
@@ -166,7 +171,7 @@ export async function getVerifyClaimResponse(model, sources, answer) {
|
|
|
166
171
|
* - `DUMP_HALLOUMI_REQ_FILE_PATH`: If set, the LLM request (URL and parameters) is dumped to the specified file path.
|
|
167
172
|
* - `DUMP_HALLOUMI_FILE_PATH`: If set, the LLM response is dumped to the specified file path.
|
|
168
173
|
*/
|
|
169
|
-
async function getLLMResponse(model, prompt) {
|
|
174
|
+
async function getLLMResponse(model, prompt, { ip } = {}) {
|
|
170
175
|
let jsonData;
|
|
171
176
|
|
|
172
177
|
if (process.env.MOCK_HALLOUMI_FILE_PATH) {
|
|
@@ -194,7 +199,7 @@ async function getLLMResponse(model, prompt) {
|
|
|
194
199
|
log(`Dumped halloumi request: ${filePath}`);
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
jsonData = await callLLM(model.apiUrl, model.apiKey, data);
|
|
202
|
+
jsonData = await callLLM(model.apiUrl, model.apiKey, data, { ip });
|
|
198
203
|
|
|
199
204
|
if (process.env.DUMP_HALLOUMI_FILE_PATH) {
|
|
200
205
|
const filePath = process.env.DUMP_HALLOUMI_FILE_PATH;
|
|
@@ -210,8 +215,8 @@ async function getLLMResponse(model, prompt) {
|
|
|
210
215
|
* @param response A string containing all claims and their information.
|
|
211
216
|
* @returns A list of claim objects.
|
|
212
217
|
*/
|
|
213
|
-
export async function halloumiGenerativeAPI(model, prompt) {
|
|
214
|
-
const jsonData = await getLLMResponse(model, prompt);
|
|
218
|
+
export async function halloumiGenerativeAPI(model, prompt, { ip } = {}) {
|
|
219
|
+
const jsonData = await getLLMResponse(model, prompt, { ip });
|
|
215
220
|
|
|
216
221
|
// Todo: restore log
|
|
217
222
|
// log('Generative response', jsonData);
|
package/src/middleware.js
CHANGED
|
@@ -151,6 +151,7 @@ async function send_onyx_request(
|
|
|
151
151
|
res,
|
|
152
152
|
{ username, password, api_key, url, is_related_question },
|
|
153
153
|
) {
|
|
154
|
+
const forwardedFor = req.headers['x-forwarded-for'] || req.ip;
|
|
154
155
|
let headers = {};
|
|
155
156
|
if (!api_key) {
|
|
156
157
|
await login(username, password);
|
|
@@ -166,11 +167,13 @@ async function send_onyx_request(
|
|
|
166
167
|
headers = {
|
|
167
168
|
Cookie: cached_auth_cookie,
|
|
168
169
|
'Content-Type': 'application/json',
|
|
170
|
+
'X-Forwarded-For': forwardedFor,
|
|
169
171
|
};
|
|
170
172
|
} else {
|
|
171
173
|
headers = {
|
|
172
174
|
Authorization: 'Bearer ' + api_key,
|
|
173
175
|
'Content-Type': 'application/json',
|
|
176
|
+
'X-Forwarded-For': forwardedFor,
|
|
174
177
|
};
|
|
175
178
|
}
|
|
176
179
|
|