bunnyquery 1.3.5 → 1.4.0
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 +76 -7
- package/bunnyquery.js +137 -44
- package/dist/engine.cjs +135 -40
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +70 -1
- package/dist/engine.d.ts +70 -1
- package/dist/engine.mjs +130 -41
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/attachment_parsers.ts +112 -0
- package/src/engine/config.ts +11 -0
- package/src/engine/index.ts +12 -0
- package/src/engine/office.ts +26 -4
- package/src/engine/prompts/indexing_user_message.ts +20 -0
- package/src/engine/requests.ts +17 -5
- package/src/engine/session.ts +9 -0
package/dist/engine.d.mts
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side attachment-parser plugins.
|
|
3
|
+
*
|
|
4
|
+
* Some attachment formats can't be read by the model's web_fetch (binary) and
|
|
5
|
+
* have no server-side extractor either — e.g. legacy Hancom .hwp. A parser
|
|
6
|
+
* plugin runs IN THE BROWSER, turns the uploaded File into indexable text (or an
|
|
7
|
+
* HTML string), and the engine sends that content INLINE in the background
|
|
8
|
+
* indexing request — so the model indexes the parsed content directly, with no
|
|
9
|
+
* upload-side server extraction and no web_fetch for that file.
|
|
10
|
+
*
|
|
11
|
+
* Register a parser with `registerAttachmentParser()`, or via
|
|
12
|
+
* `configureChatEngine({ attachmentParsers: [...] })`. The BunnyQuery widget also
|
|
13
|
+
* exposes `BunnyQuery.registerAttachmentParser()` and an `attachmentParsers`
|
|
14
|
+
* init option. First matching parser wins.
|
|
15
|
+
*/
|
|
16
|
+
interface AttachmentParser {
|
|
17
|
+
/** Human-readable label — used only in logs. */
|
|
18
|
+
name?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Return true if this parser handles the file. Receives the file name and
|
|
21
|
+
* (when known) its MIME type. Keep it cheap — it runs for every upload.
|
|
22
|
+
*/
|
|
23
|
+
match: (file: {
|
|
24
|
+
name: string;
|
|
25
|
+
mime?: string;
|
|
26
|
+
}) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Parse the File into indexable plain text OR an HTML string (the model reads
|
|
29
|
+
* either). Runs in the browser; may be async. Return a falsy/empty value to
|
|
30
|
+
* skip (the file then falls back to web_fetch / server extraction).
|
|
31
|
+
*/
|
|
32
|
+
parse: (file: File) => string | null | undefined | Promise<string | null | undefined>;
|
|
33
|
+
}
|
|
34
|
+
declare const MAX_PARSED_CONTENT_CHARS = 200000;
|
|
35
|
+
/** Register an attachment parser. Ignores duplicates (by reference) and invalid plugins. */
|
|
36
|
+
declare function registerAttachmentParser(parser: AttachmentParser): void;
|
|
37
|
+
/** Remove all registered parsers (mainly for tests / re-init). */
|
|
38
|
+
declare function clearAttachmentParsers(): void;
|
|
39
|
+
/** Snapshot of the registered parsers. */
|
|
40
|
+
declare function getAttachmentParsers(): AttachmentParser[];
|
|
41
|
+
/** First parser whose `match` returns true for the given file, if any. */
|
|
42
|
+
declare function findAttachmentParser(name: string, mime?: string): AttachmentParser | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Run the matching parser (if any) and return capped, trimmed content — or null
|
|
45
|
+
* when there is no parser, the parser throws, or it yields nothing. Never throws:
|
|
46
|
+
* a parser failure degrades to null so the upload still completes (the file then
|
|
47
|
+
* resolves via its normal path).
|
|
48
|
+
*/
|
|
49
|
+
declare function parseAttachmentContent(file: File, name: string, mime?: string): Promise<string | null>;
|
|
50
|
+
|
|
1
51
|
/**
|
|
2
52
|
* Engine configuration / dependency injection.
|
|
3
53
|
*
|
|
@@ -12,6 +62,7 @@
|
|
|
12
62
|
* `poll: 0` to get the early ack + a manual `.poll()` handle (needed for queued-
|
|
13
63
|
* send cancel). So the request builders include `poll` only when it is set.
|
|
14
64
|
*/
|
|
65
|
+
|
|
15
66
|
interface ChatEngineConfig {
|
|
16
67
|
/** skapi.clientSecretRequest, bound to the consumer's skapi instance. */
|
|
17
68
|
clientSecretRequest: (opts: any) => Promise<any>;
|
|
@@ -24,6 +75,12 @@ interface ChatEngineConfig {
|
|
|
24
75
|
* the `poll` key is omitted entirely (agent.vue). BunnyQuery sets `0`.
|
|
25
76
|
*/
|
|
26
77
|
poll?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Optional client-side attachment parsers (e.g. an .hwp parser). Each is
|
|
80
|
+
* registered at configure time; more can be added later via
|
|
81
|
+
* `registerAttachmentParser()`. See attachment_parsers.ts.
|
|
82
|
+
*/
|
|
83
|
+
attachmentParsers?: AttachmentParser[];
|
|
27
84
|
}
|
|
28
85
|
declare function configureChatEngine(config: ChatEngineConfig): void;
|
|
29
86
|
declare function chatEngineConfig(): ChatEngineConfig;
|
|
@@ -153,6 +210,12 @@ type BuildIndexingUserMessageOptions = {
|
|
|
153
210
|
* drops the temporary-URL line — there is nothing for the model to fetch).
|
|
154
211
|
*/
|
|
155
212
|
inlineContentPlaceholder?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Actual file content parsed CLIENT-SIDE by an attachment-parser plugin (e.g.
|
|
215
|
+
* an .hwp parser). Embedded inline verbatim — no server extraction and no
|
|
216
|
+
* web_fetch for this file. Takes precedence over `inlineContentPlaceholder`.
|
|
217
|
+
*/
|
|
218
|
+
inlineContent?: string;
|
|
156
219
|
};
|
|
157
220
|
declare function buildIndexingUserMessage(attachment: IndexingAttachmentInfo, options?: BuildIndexingUserMessageOptions): string;
|
|
158
221
|
|
|
@@ -293,6 +356,12 @@ type AttachmentSaveInfo = {
|
|
|
293
356
|
size?: number;
|
|
294
357
|
url: string;
|
|
295
358
|
};
|
|
359
|
+
/**
|
|
360
|
+
* Content parsed CLIENT-SIDE by an attachment-parser plugin (e.g. an .hwp
|
|
361
|
+
* parser). When set, it is inlined into the indexing message verbatim and
|
|
362
|
+
* takes precedence over server-side office extraction / web_fetch.
|
|
363
|
+
*/
|
|
364
|
+
parsedContent?: string;
|
|
296
365
|
};
|
|
297
366
|
declare function notifyAgentSaveAttachment(info: AttachmentSaveInfo): Promise<any>;
|
|
298
367
|
declare function extractClaudeText(response: any): any;
|
|
@@ -496,4 +565,4 @@ declare class ChatSession {
|
|
|
496
565
|
bumpGate(): void;
|
|
497
566
|
}
|
|
498
567
|
|
|
499
|
-
export { type AttachmentFailureGroup, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, type BgTaskEntry, type BoundedChatOptions, type BuildIndexingUserMessageOptions, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, type ExtractDirective, HISTORY_TOKEN_BUDGET, type IndexingAttachmentInfo, type IndexingSystemPromptParams, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
|
568
|
+
export { type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, type BgTaskEntry, type BoundedChatOptions, type BuildIndexingUserMessageOptions, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, type ExtractDirective, HISTORY_TOKEN_BUDGET, type IndexingAttachmentInfo, type IndexingSystemPromptParams, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, clearAttachmentParsers, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, findAttachmentParser, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, parseAttachmentContent, registerAttachmentParser, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side attachment-parser plugins.
|
|
3
|
+
*
|
|
4
|
+
* Some attachment formats can't be read by the model's web_fetch (binary) and
|
|
5
|
+
* have no server-side extractor either — e.g. legacy Hancom .hwp. A parser
|
|
6
|
+
* plugin runs IN THE BROWSER, turns the uploaded File into indexable text (or an
|
|
7
|
+
* HTML string), and the engine sends that content INLINE in the background
|
|
8
|
+
* indexing request — so the model indexes the parsed content directly, with no
|
|
9
|
+
* upload-side server extraction and no web_fetch for that file.
|
|
10
|
+
*
|
|
11
|
+
* Register a parser with `registerAttachmentParser()`, or via
|
|
12
|
+
* `configureChatEngine({ attachmentParsers: [...] })`. The BunnyQuery widget also
|
|
13
|
+
* exposes `BunnyQuery.registerAttachmentParser()` and an `attachmentParsers`
|
|
14
|
+
* init option. First matching parser wins.
|
|
15
|
+
*/
|
|
16
|
+
interface AttachmentParser {
|
|
17
|
+
/** Human-readable label — used only in logs. */
|
|
18
|
+
name?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Return true if this parser handles the file. Receives the file name and
|
|
21
|
+
* (when known) its MIME type. Keep it cheap — it runs for every upload.
|
|
22
|
+
*/
|
|
23
|
+
match: (file: {
|
|
24
|
+
name: string;
|
|
25
|
+
mime?: string;
|
|
26
|
+
}) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Parse the File into indexable plain text OR an HTML string (the model reads
|
|
29
|
+
* either). Runs in the browser; may be async. Return a falsy/empty value to
|
|
30
|
+
* skip (the file then falls back to web_fetch / server extraction).
|
|
31
|
+
*/
|
|
32
|
+
parse: (file: File) => string | null | undefined | Promise<string | null | undefined>;
|
|
33
|
+
}
|
|
34
|
+
declare const MAX_PARSED_CONTENT_CHARS = 200000;
|
|
35
|
+
/** Register an attachment parser. Ignores duplicates (by reference) and invalid plugins. */
|
|
36
|
+
declare function registerAttachmentParser(parser: AttachmentParser): void;
|
|
37
|
+
/** Remove all registered parsers (mainly for tests / re-init). */
|
|
38
|
+
declare function clearAttachmentParsers(): void;
|
|
39
|
+
/** Snapshot of the registered parsers. */
|
|
40
|
+
declare function getAttachmentParsers(): AttachmentParser[];
|
|
41
|
+
/** First parser whose `match` returns true for the given file, if any. */
|
|
42
|
+
declare function findAttachmentParser(name: string, mime?: string): AttachmentParser | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Run the matching parser (if any) and return capped, trimmed content — or null
|
|
45
|
+
* when there is no parser, the parser throws, or it yields nothing. Never throws:
|
|
46
|
+
* a parser failure degrades to null so the upload still completes (the file then
|
|
47
|
+
* resolves via its normal path).
|
|
48
|
+
*/
|
|
49
|
+
declare function parseAttachmentContent(file: File, name: string, mime?: string): Promise<string | null>;
|
|
50
|
+
|
|
1
51
|
/**
|
|
2
52
|
* Engine configuration / dependency injection.
|
|
3
53
|
*
|
|
@@ -12,6 +62,7 @@
|
|
|
12
62
|
* `poll: 0` to get the early ack + a manual `.poll()` handle (needed for queued-
|
|
13
63
|
* send cancel). So the request builders include `poll` only when it is set.
|
|
14
64
|
*/
|
|
65
|
+
|
|
15
66
|
interface ChatEngineConfig {
|
|
16
67
|
/** skapi.clientSecretRequest, bound to the consumer's skapi instance. */
|
|
17
68
|
clientSecretRequest: (opts: any) => Promise<any>;
|
|
@@ -24,6 +75,12 @@ interface ChatEngineConfig {
|
|
|
24
75
|
* the `poll` key is omitted entirely (agent.vue). BunnyQuery sets `0`.
|
|
25
76
|
*/
|
|
26
77
|
poll?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Optional client-side attachment parsers (e.g. an .hwp parser). Each is
|
|
80
|
+
* registered at configure time; more can be added later via
|
|
81
|
+
* `registerAttachmentParser()`. See attachment_parsers.ts.
|
|
82
|
+
*/
|
|
83
|
+
attachmentParsers?: AttachmentParser[];
|
|
27
84
|
}
|
|
28
85
|
declare function configureChatEngine(config: ChatEngineConfig): void;
|
|
29
86
|
declare function chatEngineConfig(): ChatEngineConfig;
|
|
@@ -153,6 +210,12 @@ type BuildIndexingUserMessageOptions = {
|
|
|
153
210
|
* drops the temporary-URL line — there is nothing for the model to fetch).
|
|
154
211
|
*/
|
|
155
212
|
inlineContentPlaceholder?: string;
|
|
213
|
+
/**
|
|
214
|
+
* Actual file content parsed CLIENT-SIDE by an attachment-parser plugin (e.g.
|
|
215
|
+
* an .hwp parser). Embedded inline verbatim — no server extraction and no
|
|
216
|
+
* web_fetch for this file. Takes precedence over `inlineContentPlaceholder`.
|
|
217
|
+
*/
|
|
218
|
+
inlineContent?: string;
|
|
156
219
|
};
|
|
157
220
|
declare function buildIndexingUserMessage(attachment: IndexingAttachmentInfo, options?: BuildIndexingUserMessageOptions): string;
|
|
158
221
|
|
|
@@ -293,6 +356,12 @@ type AttachmentSaveInfo = {
|
|
|
293
356
|
size?: number;
|
|
294
357
|
url: string;
|
|
295
358
|
};
|
|
359
|
+
/**
|
|
360
|
+
* Content parsed CLIENT-SIDE by an attachment-parser plugin (e.g. an .hwp
|
|
361
|
+
* parser). When set, it is inlined into the indexing message verbatim and
|
|
362
|
+
* takes precedence over server-side office extraction / web_fetch.
|
|
363
|
+
*/
|
|
364
|
+
parsedContent?: string;
|
|
296
365
|
};
|
|
297
366
|
declare function notifyAgentSaveAttachment(info: AttachmentSaveInfo): Promise<any>;
|
|
298
367
|
declare function extractClaudeText(response: any): any;
|
|
@@ -496,4 +565,4 @@ declare class ChatSession {
|
|
|
496
565
|
bumpGate(): void;
|
|
497
566
|
}
|
|
498
567
|
|
|
499
|
-
export { type AttachmentFailureGroup, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, type BgTaskEntry, type BoundedChatOptions, type BuildIndexingUserMessageOptions, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, type ExtractDirective, HISTORY_TOKEN_BUDGET, type IndexingAttachmentInfo, type IndexingSystemPromptParams, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
|
568
|
+
export { type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, type BgTaskEntry, type BoundedChatOptions, type BuildIndexingUserMessageOptions, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, type ExtractDirective, HISTORY_TOKEN_BUDGET, type IndexingAttachmentInfo, type IndexingSystemPromptParams, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, clearAttachmentParsers, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, findAttachmentParser, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, parseAttachmentContent, registerAttachmentParser, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
package/dist/engine.mjs
CHANGED
|
@@ -1,7 +1,55 @@
|
|
|
1
|
+
// src/engine/attachment_parsers.ts
|
|
2
|
+
var MAX_PARSED_CONTENT_CHARS = 2e5;
|
|
3
|
+
var _parsers = [];
|
|
4
|
+
function registerAttachmentParser(parser) {
|
|
5
|
+
if (parser && typeof parser.match === "function" && typeof parser.parse === "function" && _parsers.indexOf(parser) === -1) {
|
|
6
|
+
_parsers.push(parser);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function clearAttachmentParsers() {
|
|
10
|
+
_parsers.length = 0;
|
|
11
|
+
}
|
|
12
|
+
function getAttachmentParsers() {
|
|
13
|
+
return _parsers.slice();
|
|
14
|
+
}
|
|
15
|
+
function findAttachmentParser(name, mime) {
|
|
16
|
+
for (let i = 0; i < _parsers.length; i++) {
|
|
17
|
+
try {
|
|
18
|
+
if (_parsers[i].match({ name, mime })) return _parsers[i];
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
async function parseAttachmentContent(file, name, mime) {
|
|
25
|
+
const parser = findAttachmentParser(name, mime);
|
|
26
|
+
if (!parser) return null;
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = await parser.parse(file);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error(
|
|
32
|
+
`[chat-engine] attachment parser ${parser.name || "(unnamed)"} failed for ${name}:`,
|
|
33
|
+
err
|
|
34
|
+
);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
let text = (raw == null ? "" : String(raw)).trim();
|
|
38
|
+
if (!text) return null;
|
|
39
|
+
if (text.length > MAX_PARSED_CONTENT_CHARS) {
|
|
40
|
+
text = text.slice(0, MAX_PARSED_CONTENT_CHARS) + `
|
|
41
|
+
...[truncated for length; original ${text.length} characters]`;
|
|
42
|
+
}
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
|
|
1
46
|
// src/engine/config.ts
|
|
2
47
|
var _config = null;
|
|
3
48
|
function configureChatEngine(config) {
|
|
4
49
|
_config = config;
|
|
50
|
+
if (config.attachmentParsers) {
|
|
51
|
+
for (const parser of config.attachmentParsers) registerAttachmentParser(parser);
|
|
52
|
+
}
|
|
5
53
|
}
|
|
6
54
|
function chatEngineConfig() {
|
|
7
55
|
if (!_config) {
|
|
@@ -28,13 +76,41 @@ var OFFICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
28
76
|
"pptx",
|
|
29
77
|
"pptm",
|
|
30
78
|
"hwp",
|
|
31
|
-
"hwpx"
|
|
79
|
+
"hwpx",
|
|
80
|
+
"ods",
|
|
81
|
+
"odt",
|
|
82
|
+
"odp",
|
|
83
|
+
"epub"
|
|
84
|
+
]);
|
|
85
|
+
var WEB_FETCHABLE_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
86
|
+
"csv",
|
|
87
|
+
"tsv",
|
|
88
|
+
"tab",
|
|
89
|
+
"txt",
|
|
90
|
+
"text",
|
|
91
|
+
"md",
|
|
92
|
+
"markdown",
|
|
93
|
+
"json",
|
|
94
|
+
"ndjson",
|
|
95
|
+
"jsonl",
|
|
96
|
+
"xml",
|
|
97
|
+
"yaml",
|
|
98
|
+
"yml",
|
|
99
|
+
"log",
|
|
100
|
+
// RTF is a TEXT format (not a binary zip), so web_fetch can read it and the
|
|
101
|
+
// model decodes its control words. Pin it here so a `.rtf` reported as
|
|
102
|
+
// `application/msword` isn't misrouted to server-side extraction (which has no
|
|
103
|
+
// .rtf extractor → "unsupported format" note).
|
|
104
|
+
"rtf",
|
|
105
|
+
"htm",
|
|
106
|
+
"html"
|
|
32
107
|
]);
|
|
33
108
|
function isOfficeFile(name, mime) {
|
|
34
109
|
const ext = (name || "").split(".").pop()?.toLowerCase() || "";
|
|
35
110
|
if (OFFICE_FILE_EXTENSIONS.has(ext)) return true;
|
|
111
|
+
if (WEB_FETCHABLE_TEXT_EXTENSIONS.has(ext)) return false;
|
|
36
112
|
const m = (mime || "").toLowerCase();
|
|
37
|
-
return m.includes("officedocument") || m.includes("hwp") || m === "application/msword" || m === "application/vnd.ms-excel" || m === "application/vnd.ms-powerpoint";
|
|
113
|
+
return m.includes("officedocument") || m.includes("opendocument") || m.includes("hwp") || m.includes("epub") || m === "application/msword" || m === "application/vnd.ms-excel" || m === "application/vnd.ms-powerpoint";
|
|
38
114
|
}
|
|
39
115
|
var _extractPlaceholderSeq = 0;
|
|
40
116
|
function makeExtractPlaceholder(seed) {
|
|
@@ -155,6 +231,14 @@ File metadata:
|
|
|
155
231
|
` + (attachment.mime ? `- mime type: ${attachment.mime}
|
|
156
232
|
` : "") + (typeof attachment.size === "number" ? `- size (bytes): ${attachment.size}
|
|
157
233
|
` : "");
|
|
234
|
+
if (options?.inlineContent) {
|
|
235
|
+
return head + `
|
|
236
|
+
The file's content was parsed by the client and is provided inline below. Read it directly \u2014 do NOT fetch any URL for this file. Use the storage path above (not this content) for the "src::" unique_id.
|
|
237
|
+
|
|
238
|
+
----- BEGIN FILE CONTENT -----
|
|
239
|
+
${options.inlineContent}
|
|
240
|
+
----- END FILE CONTENT -----`;
|
|
241
|
+
}
|
|
158
242
|
if (options?.inlineContentPlaceholder) {
|
|
159
243
|
return head + `
|
|
160
244
|
The file's text content was extracted on the server and is provided inline below. Read it directly \u2014 do NOT fetch any URL for this file. Use the storage path above (not this content) for the "src::" unique_id.
|
|
@@ -653,14 +737,14 @@ async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system,
|
|
|
653
737
|
});
|
|
654
738
|
}
|
|
655
739
|
async function notifyAgentSaveAttachment(info) {
|
|
656
|
-
const { platform, service, owner, attachment } = info;
|
|
657
|
-
const office = isOfficeFile(attachment.name, attachment.mime);
|
|
740
|
+
const { platform, service, owner, attachment, parsedContent } = info;
|
|
741
|
+
const office = !parsedContent && isOfficeFile(attachment.name, attachment.mime);
|
|
658
742
|
const placeholder = office ? makeExtractPlaceholder(attachment.storagePath) : void 0;
|
|
659
743
|
const extractContent = office && placeholder ? [{ path: attachment.storagePath, placeholder, name: attachment.name, mime: attachment.mime }] : void 0;
|
|
660
744
|
const skapiExtract = extractContent && extractContent.length ? { _skapi_extract: extractContent } : {};
|
|
661
745
|
const userMessage = buildIndexingUserMessage(
|
|
662
746
|
attachment,
|
|
663
|
-
placeholder ? { inlineContentPlaceholder: placeholder } : void 0
|
|
747
|
+
parsedContent ? { inlineContent: parsedContent } : placeholder ? { inlineContentPlaceholder: placeholder } : void 0
|
|
664
748
|
);
|
|
665
749
|
const systemPrompt = buildIndexingSystemPrompt({
|
|
666
750
|
service,
|
|
@@ -1862,44 +1946,49 @@ var ChatSession = class {
|
|
|
1862
1946
|
att.storagePath = member.storagePath;
|
|
1863
1947
|
}
|
|
1864
1948
|
var mime = member.file.type || self.host.getMimeType(member.file.name);
|
|
1865
|
-
return
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
url
|
|
1879
|
-
}
|
|
1880
|
-
}).then(function(ack) {
|
|
1881
|
-
if (ack && typeof ack.id === "string") {
|
|
1882
|
-
self.bgTaskQueue.push({
|
|
1883
|
-
serviceId: id.serviceId,
|
|
1884
|
-
platform: id.platform,
|
|
1885
|
-
id: ack.id,
|
|
1886
|
-
filename: member.file.name,
|
|
1949
|
+
return Promise.resolve(
|
|
1950
|
+
parseAttachmentContent(member.file, member.file.name, mime || void 0)
|
|
1951
|
+
).then(function(parsedContent) {
|
|
1952
|
+
return notifyAgentSaveAttachment({
|
|
1953
|
+
platform: id.platform,
|
|
1954
|
+
model: id.model,
|
|
1955
|
+
service: id.serviceId,
|
|
1956
|
+
owner: id.owner,
|
|
1957
|
+
userId: id.userId || id.serviceId,
|
|
1958
|
+
serviceName: id.serviceName,
|
|
1959
|
+
serviceDescription: id.serviceDescription,
|
|
1960
|
+
attachment: {
|
|
1961
|
+
name: member.file.name,
|
|
1887
1962
|
storagePath: member.storagePath,
|
|
1888
|
-
isReindex: hadExists,
|
|
1889
1963
|
mime: mime || void 0,
|
|
1890
1964
|
size: member.file.size,
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1965
|
+
url
|
|
1966
|
+
},
|
|
1967
|
+
parsedContent: parsedContent || void 0
|
|
1968
|
+
}).then(function(ack) {
|
|
1969
|
+
if (ack && typeof ack.id === "string") {
|
|
1970
|
+
self.bgTaskQueue.push({
|
|
1971
|
+
serviceId: id.serviceId,
|
|
1972
|
+
platform: id.platform,
|
|
1973
|
+
id: ack.id,
|
|
1974
|
+
filename: member.file.name,
|
|
1975
|
+
storagePath: member.storagePath,
|
|
1976
|
+
isReindex: hadExists,
|
|
1977
|
+
mime: mime || void 0,
|
|
1978
|
+
size: member.file.size,
|
|
1979
|
+
status: ack.status === "running" ? "running" : "pending",
|
|
1980
|
+
poll: ack.poll
|
|
1981
|
+
});
|
|
1982
|
+
self.drainBgTaskQueue();
|
|
1983
|
+
}
|
|
1984
|
+
}, function(e) {
|
|
1985
|
+
console.error("[chat-engine] indexing request failed", e);
|
|
1986
|
+
anyIndexFailed = true;
|
|
1987
|
+
if (!att.errorCode && !att.errorDetail) {
|
|
1988
|
+
att.errorCode = e && (e.code || e.body && e.body.code) || "";
|
|
1989
|
+
att.errorDetail = e && (e.message || e.body && e.body.message) || (typeof e === "string" ? e : "");
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1903
1992
|
});
|
|
1904
1993
|
});
|
|
1905
1994
|
});
|
|
@@ -1979,6 +2068,6 @@ var ChatSession = class {
|
|
|
1979
2068
|
}
|
|
1980
2069
|
};
|
|
1981
2070
|
|
|
1982
|
-
export { BG_INDEXING_QUEUE_SUFFIX, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, HISTORY_TOKEN_BUDGET, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
|
2071
|
+
export { BG_INDEXING_QUEUE_SUFFIX, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, HISTORY_TOKEN_BUDGET, LINK_LABEL_MAX_DISPLAY_CHARS, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingSystemPrompt, buildIndexingUserMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, clearAttachmentParsers, composeUserMessage, configureChatEngine, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, filterListByClearHorizon, findAttachmentParser, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isErrorResponseBody, isNonRetryableRequestError, isOfficeFile, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, notifyAgentSaveAttachment, parseAttachmentContent, registerAttachmentParser, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
|
1983
2072
|
//# sourceMappingURL=engine.mjs.map
|
|
1984
2073
|
//# sourceMappingURL=engine.mjs.map
|