@ixo/common 1.1.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/.eslintrc.js +9 -0
- package/.prettierignore +3 -0
- package/.prettierrc.cjs +4 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +76 -0
- package/README.md +245 -0
- package/dist/ai/checkpointer/index.d.ts +2 -0
- package/dist/ai/checkpointer/index.d.ts.map +1 -0
- package/dist/ai/checkpointer/index.js +2 -0
- package/dist/ai/checkpointer/index.js.map +1 -0
- package/dist/ai/index.d.ts +9 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +9 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/models/index.d.ts +2 -0
- package/dist/ai/models/index.d.ts.map +1 -0
- package/dist/ai/models/index.js +2 -0
- package/dist/ai/models/index.js.map +1 -0
- package/dist/ai/models/openai.d.ts +10 -0
- package/dist/ai/models/openai.d.ts.map +1 -0
- package/dist/ai/models/openai.js +38 -0
- package/dist/ai/models/openai.js.map +1 -0
- package/dist/ai/models/openai.test.d.ts +2 -0
- package/dist/ai/models/openai.test.d.ts.map +1 -0
- package/dist/ai/models/openai.test.js +58 -0
- package/dist/ai/models/openai.test.js.map +1 -0
- package/dist/ai/nodes/create-fake-node.d.ts +2 -0
- package/dist/ai/nodes/create-fake-node.d.ts.map +1 -0
- package/dist/ai/nodes/create-fake-node.js +2 -0
- package/dist/ai/nodes/create-fake-node.js.map +1 -0
- package/dist/ai/nodes/find-docs/find-docs.prompt.d.ts +3 -0
- package/dist/ai/nodes/find-docs/find-docs.prompt.d.ts.map +1 -0
- package/dist/ai/nodes/find-docs/find-docs.prompt.js +61 -0
- package/dist/ai/nodes/find-docs/find-docs.prompt.js.map +1 -0
- package/dist/ai/nodes/find-docs/index.d.ts +3 -0
- package/dist/ai/nodes/find-docs/index.d.ts.map +1 -0
- package/dist/ai/nodes/find-docs/index.js +3 -0
- package/dist/ai/nodes/find-docs/index.js.map +1 -0
- package/dist/ai/nodes/find-docs/node.d.ts +17 -0
- package/dist/ai/nodes/find-docs/node.d.ts.map +1 -0
- package/dist/ai/nodes/find-docs/node.js +46 -0
- package/dist/ai/nodes/find-docs/node.js.map +1 -0
- package/dist/ai/nodes/generic-chat/generic-chat.node.d.ts +12 -0
- package/dist/ai/nodes/generic-chat/generic-chat.node.d.ts.map +1 -0
- package/dist/ai/nodes/generic-chat/generic-chat.node.js +34 -0
- package/dist/ai/nodes/generic-chat/generic-chat.node.js.map +1 -0
- package/dist/ai/nodes/generic-chat/generic-chat.prompt.d.ts +10 -0
- package/dist/ai/nodes/generic-chat/generic-chat.prompt.d.ts.map +1 -0
- package/dist/ai/nodes/generic-chat/generic-chat.prompt.js +58 -0
- package/dist/ai/nodes/generic-chat/generic-chat.prompt.js.map +1 -0
- package/dist/ai/nodes/generic-chat/index.d.ts +2 -0
- package/dist/ai/nodes/generic-chat/index.d.ts.map +1 -0
- package/dist/ai/nodes/generic-chat/index.js +2 -0
- package/dist/ai/nodes/generic-chat/index.js.map +1 -0
- package/dist/ai/nodes/index.d.ts +4 -0
- package/dist/ai/nodes/index.d.ts.map +1 -0
- package/dist/ai/nodes/index.js +4 -0
- package/dist/ai/nodes/index.js.map +1 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts +7 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.d.ts.map +1 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.js +71 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.js.map +1 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.test.d.ts +2 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.test.d.ts.map +1 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.test.js +68 -0
- package/dist/ai/semantic-router-factory/create-semantic-router.test.js.map +1 -0
- package/dist/ai/semantic-router-factory/index.d.ts +4 -0
- package/dist/ai/semantic-router-factory/index.d.ts.map +1 -0
- package/dist/ai/semantic-router-factory/index.js +4 -0
- package/dist/ai/semantic-router-factory/index.js.map +1 -0
- package/dist/ai/semantic-router-factory/semantic-router-prompt.d.ts +2 -0
- package/dist/ai/semantic-router-factory/semantic-router-prompt.d.ts.map +1 -0
- package/dist/ai/semantic-router-factory/semantic-router-prompt.js +169 -0
- package/dist/ai/semantic-router-factory/semantic-router-prompt.js.map +1 -0
- package/dist/ai/semantic-router-factory/validate-routes.d.ts +2 -0
- package/dist/ai/semantic-router-factory/validate-routes.d.ts.map +1 -0
- package/dist/ai/semantic-router-factory/validate-routes.js +16 -0
- package/dist/ai/semantic-router-factory/validate-routes.js.map +1 -0
- package/dist/ai/tools/action-caller.d.ts +14 -0
- package/dist/ai/tools/action-caller.d.ts.map +1 -0
- package/dist/ai/tools/action-caller.js +12 -0
- package/dist/ai/tools/action-caller.js.map +1 -0
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts +25 -0
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.d.ts.map +1 -0
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.js +46 -0
- package/dist/ai/tools/ask-ixo-guru/ask-ixo-guru.js.map +1 -0
- package/dist/ai/tools/ask-ixo-guru/index.d.ts +2 -0
- package/dist/ai/tools/ask-ixo-guru/index.d.ts.map +1 -0
- package/dist/ai/tools/ask-ixo-guru/index.js +2 -0
- package/dist/ai/tools/ask-ixo-guru/index.js.map +1 -0
- package/dist/ai/tools/browser-tool-caller.d.ts +14 -0
- package/dist/ai/tools/browser-tool-caller.d.ts.map +1 -0
- package/dist/ai/tools/browser-tool-caller.js +12 -0
- package/dist/ai/tools/browser-tool-caller.js.map +1 -0
- package/dist/ai/tools/frontend-tool-caller.d.ts +10 -0
- package/dist/ai/tools/frontend-tool-caller.d.ts.map +1 -0
- package/dist/ai/tools/frontend-tool-caller.js +50 -0
- package/dist/ai/tools/frontend-tool-caller.js.map +1 -0
- package/dist/ai/tools/index.d.ts +11 -0
- package/dist/ai/tools/index.d.ts.map +1 -0
- package/dist/ai/tools/index.js +11 -0
- package/dist/ai/tools/index.js.map +1 -0
- package/dist/ai/tools/log-action-to-matrix.d.ts +13 -0
- package/dist/ai/tools/log-action-to-matrix.d.ts.map +1 -0
- package/dist/ai/tools/log-action-to-matrix.js +14 -0
- package/dist/ai/tools/log-action-to-matrix.js.map +1 -0
- package/dist/ai/tools/parser-action-tool.d.ts +8 -0
- package/dist/ai/tools/parser-action-tool.d.ts.map +1 -0
- package/dist/ai/tools/parser-action-tool.js +40 -0
- package/dist/ai/tools/parser-action-tool.js.map +1 -0
- package/dist/ai/tools/parser-browser-tool.d.ts +8 -0
- package/dist/ai/tools/parser-browser-tool.d.ts.map +1 -0
- package/dist/ai/tools/parser-browser-tool.js +38 -0
- package/dist/ai/tools/parser-browser-tool.js.map +1 -0
- package/dist/ai/tools/retriever-tool/index.d.ts +2 -0
- package/dist/ai/tools/retriever-tool/index.d.ts.map +1 -0
- package/dist/ai/tools/retriever-tool/index.js +2 -0
- package/dist/ai/tools/retriever-tool/index.js.map +1 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.d.ts +18 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.d.ts.map +1 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.js +62 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.js.map +1 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.test.d.ts +2 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.test.d.ts.map +1 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.test.js +119 -0
- package/dist/ai/tools/retriever-tool/retriever-tool.test.js.map +1 -0
- package/dist/ai/tools/scrape-web-page.d.ts +7 -0
- package/dist/ai/tools/scrape-web-page.d.ts.map +1 -0
- package/dist/ai/tools/scrape-web-page.js +65 -0
- package/dist/ai/tools/scrape-web-page.js.map +1 -0
- package/dist/ai/tools/web-search-tool.d.ts +10 -0
- package/dist/ai/tools/web-search-tool.d.ts.map +1 -0
- package/dist/ai/tools/web-search-tool.js +30 -0
- package/dist/ai/tools/web-search-tool.js.map +1 -0
- package/dist/ai/types.d.ts +4 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/ai/utils/__tests__/chunk-arr.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/chunk-arr.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/chunk-arr.test.js +37 -0
- package/dist/ai/utils/__tests__/chunk-arr.test.js.map +1 -0
- package/dist/ai/utils/__tests__/doc-relevance-checker.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/doc-relevance-checker.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/doc-relevance-checker.test.js +80 -0
- package/dist/ai/utils/__tests__/doc-relevance-checker.test.js.map +1 -0
- package/dist/ai/utils/__tests__/doc-splitter.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/doc-splitter.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/doc-splitter.test.js +35 -0
- package/dist/ai/utils/__tests__/doc-splitter.test.js.map +1 -0
- package/dist/ai/utils/__tests__/filter-similarity-search-results.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/filter-similarity-search-results.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/filter-similarity-search-results.test.js +47 -0
- package/dist/ai/utils/__tests__/filter-similarity-search-results.test.js.map +1 -0
- package/dist/ai/utils/__tests__/json-to-yaml.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/json-to-yaml.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/json-to-yaml.test.js +63 -0
- package/dist/ai/utils/__tests__/json-to-yaml.test.js.map +1 -0
- package/dist/ai/utils/__tests__/stringify-docs.test.d.ts +2 -0
- package/dist/ai/utils/__tests__/stringify-docs.test.d.ts.map +1 -0
- package/dist/ai/utils/__tests__/stringify-docs.test.js +54 -0
- package/dist/ai/utils/__tests__/stringify-docs.test.js.map +1 -0
- package/dist/ai/utils/chunk-arr.d.ts +3 -0
- package/dist/ai/utils/chunk-arr.d.ts.map +1 -0
- package/dist/ai/utils/chunk-arr.js +13 -0
- package/dist/ai/utils/chunk-arr.js.map +1 -0
- package/dist/ai/utils/doc-relevance-checker.d.ts +10 -0
- package/dist/ai/utils/doc-relevance-checker.d.ts.map +1 -0
- package/dist/ai/utils/doc-relevance-checker.js +37 -0
- package/dist/ai/utils/doc-relevance-checker.js.map +1 -0
- package/dist/ai/utils/doc-splitter.d.ts +3 -0
- package/dist/ai/utils/doc-splitter.d.ts.map +1 -0
- package/dist/ai/utils/doc-splitter.js +20 -0
- package/dist/ai/utils/doc-splitter.js.map +1 -0
- package/dist/ai/utils/filter-similarity-search-results.d.ts +4 -0
- package/dist/ai/utils/filter-similarity-search-results.d.ts.map +1 -0
- package/dist/ai/utils/filter-similarity-search-results.js +11 -0
- package/dist/ai/utils/filter-similarity-search-results.js.map +1 -0
- package/dist/ai/utils/generate-questions-from-chunks.d.ts +15 -0
- package/dist/ai/utils/generate-questions-from-chunks.d.ts.map +1 -0
- package/dist/ai/utils/generate-questions-from-chunks.js +101 -0
- package/dist/ai/utils/generate-questions-from-chunks.js.map +1 -0
- package/dist/ai/utils/index.d.ts +11 -0
- package/dist/ai/utils/index.d.ts.map +1 -0
- package/dist/ai/utils/index.js +11 -0
- package/dist/ai/utils/index.js.map +1 -0
- package/dist/ai/utils/json-to-yaml.d.ts +2 -0
- package/dist/ai/utils/json-to-yaml.d.ts.map +1 -0
- package/dist/ai/utils/json-to-yaml.js +25 -0
- package/dist/ai/utils/json-to-yaml.js.map +1 -0
- package/dist/ai/utils/load-file.d.ts +3 -0
- package/dist/ai/utils/load-file.d.ts.map +1 -0
- package/dist/ai/utils/load-file.js +133 -0
- package/dist/ai/utils/load-file.js.map +1 -0
- package/dist/ai/utils/stringify-docs.d.ts +3 -0
- package/dist/ai/utils/stringify-docs.d.ts.map +1 -0
- package/dist/ai/utils/stringify-docs.js +6 -0
- package/dist/ai/utils/stringify-docs.js.map +1 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts +35 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.d.ts.map +1 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js +55 -0
- package/dist/ai/utils/transformGraphStateMessageToListMessageResponse.js.map +1 -0
- package/dist/ai/utils/verify-matrix-openId-token.d.ts +6 -0
- package/dist/ai/utils/verify-matrix-openId-token.d.ts.map +1 -0
- package/dist/ai/utils/verify-matrix-openId-token.js +36 -0
- package/dist/ai/utils/verify-matrix-openId-token.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/services/env/env-service.test.d.ts +2 -0
- package/dist/services/env/env-service.test.d.ts.map +1 -0
- package/dist/services/env/env-service.test.js +99 -0
- package/dist/services/env/env-service.test.js.map +1 -0
- package/dist/services/env/env.service.d.ts +11 -0
- package/dist/services/env/env.service.d.ts.map +1 -0
- package/dist/services/env/env.service.js +42 -0
- package/dist/services/env/env.service.js.map +1 -0
- package/dist/services/env/index.d.ts +2 -0
- package/dist/services/env/index.d.ts.map +1 -0
- package/dist/services/env/index.js +2 -0
- package/dist/services/env/index.js.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +5 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/memory-engine/memory-engine.service.d.ts +35 -0
- package/dist/services/memory-engine/memory-engine.service.d.ts.map +1 -0
- package/dist/services/memory-engine/memory-engine.service.js +295 -0
- package/dist/services/memory-engine/memory-engine.service.js.map +1 -0
- package/dist/services/memory-engine/types.d.ts +88 -0
- package/dist/services/memory-engine/types.d.ts.map +1 -0
- package/dist/services/memory-engine/types.js +2 -0
- package/dist/services/memory-engine/types.js.map +1 -0
- package/dist/services/session-manager/dto.d.ts +40 -0
- package/dist/services/session-manager/dto.d.ts.map +1 -0
- package/dist/services/session-manager/dto.js +170 -0
- package/dist/services/session-manager/dto.js.map +1 -0
- package/dist/services/session-manager/errors.d.ts +22 -0
- package/dist/services/session-manager/errors.d.ts.map +1 -0
- package/dist/services/session-manager/errors.js +41 -0
- package/dist/services/session-manager/errors.js.map +1 -0
- package/dist/services/session-manager/index.d.ts +4 -0
- package/dist/services/session-manager/index.d.ts.map +1 -0
- package/dist/services/session-manager/index.js +4 -0
- package/dist/services/session-manager/index.js.map +1 -0
- package/dist/services/session-manager/session-manager.service.d.ts +40 -0
- package/dist/services/session-manager/session-manager.service.d.ts.map +1 -0
- package/dist/services/session-manager/session-manager.service.js +251 -0
- package/dist/services/session-manager/session-manager.service.js.map +1 -0
- package/dist/utils/get-user-subscription.d.ts +21 -0
- package/dist/utils/get-user-subscription.d.ts.map +1 -0
- package/dist/utils/get-user-subscription.js +44 -0
- package/dist/utils/get-user-subscription.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/docs/ai-module.md +84 -0
- package/docs/services.md +168 -0
- package/docs/tools.md +325 -0
- package/jest.config.js +6 -0
- package/package.json +81 -0
- package/src/ai/checkpointer/index.ts +1 -0
- package/src/ai/index.ts +8 -0
- package/src/ai/models/index.ts +1 -0
- package/src/ai/models/openai.test.ts +72 -0
- package/src/ai/models/openai.ts +54 -0
- package/src/ai/nodes/create-fake-node.ts +1 -0
- package/src/ai/nodes/find-docs/find-docs.prompt.ts +61 -0
- package/src/ai/nodes/find-docs/index.ts +2 -0
- package/src/ai/nodes/find-docs/node.ts +83 -0
- package/src/ai/nodes/generic-chat/generic-chat.node.ts +58 -0
- package/src/ai/nodes/generic-chat/generic-chat.prompt.ts +66 -0
- package/src/ai/nodes/generic-chat/index.ts +1 -0
- package/src/ai/nodes/index.ts +3 -0
- package/src/ai/semantic-router-factory/create-semantic-router.test.ts +98 -0
- package/src/ai/semantic-router-factory/create-semantic-router.ts +136 -0
- package/src/ai/semantic-router-factory/index.ts +3 -0
- package/src/ai/semantic-router-factory/semantic-router-prompt.ts +168 -0
- package/src/ai/semantic-router-factory/validate-routes.ts +26 -0
- package/src/ai/tools/action-caller.ts +37 -0
- package/src/ai/tools/ask-ixo-guru/ask-ixo-guru.ts +73 -0
- package/src/ai/tools/ask-ixo-guru/index.ts +1 -0
- package/src/ai/tools/browser-tool-caller.ts +37 -0
- package/src/ai/tools/frontend-tool-caller.ts +86 -0
- package/src/ai/tools/index.ts +10 -0
- package/src/ai/tools/log-action-to-matrix.ts +30 -0
- package/src/ai/tools/parser-action-tool.ts +61 -0
- package/src/ai/tools/parser-browser-tool.ts +55 -0
- package/src/ai/tools/retriever-tool/index.ts +1 -0
- package/src/ai/tools/retriever-tool/retriever-tool.test.ts +156 -0
- package/src/ai/tools/retriever-tool/retriever-tool.ts +107 -0
- package/src/ai/tools/scrape-web-page.ts +75 -0
- package/src/ai/tools/web-search-tool.ts +38 -0
- package/src/ai/types.ts +6 -0
- package/src/ai/utils/__tests__/chunk-arr.test.ts +46 -0
- package/src/ai/utils/__tests__/doc-relevance-checker.test.ts +90 -0
- package/src/ai/utils/__tests__/doc-splitter.test.ts +42 -0
- package/src/ai/utils/__tests__/filter-similarity-search-results.test.ts +57 -0
- package/src/ai/utils/__tests__/json-to-yaml.test.ts +70 -0
- package/src/ai/utils/__tests__/stringify-docs.test.ts +61 -0
- package/src/ai/utils/chunk-arr.ts +13 -0
- package/src/ai/utils/doc-relevance-checker.ts +58 -0
- package/src/ai/utils/doc-splitter.ts +28 -0
- package/src/ai/utils/filter-similarity-search-results.ts +15 -0
- package/src/ai/utils/generate-questions-from-chunks.ts +114 -0
- package/src/ai/utils/index.ts +10 -0
- package/src/ai/utils/json-to-yaml.ts +32 -0
- package/src/ai/utils/load-file.ts +170 -0
- package/src/ai/utils/stringify-docs.ts +14 -0
- package/src/ai/utils/transformGraphStateMessageToListMessageResponse.ts +108 -0
- package/src/ai/utils/verify-matrix-openId-token.ts +46 -0
- package/src/index.ts +3 -0
- package/src/services/env/env-service.test.ts +153 -0
- package/src/services/env/env.service.ts +65 -0
- package/src/services/env/index.ts +1 -0
- package/src/services/index.ts +4 -0
- package/src/services/memory-engine/memory-engine.service.ts +486 -0
- package/src/services/memory-engine/types.ts +208 -0
- package/src/services/session-manager/dto.ts +120 -0
- package/src/services/session-manager/errors.ts +56 -0
- package/src/services/session-manager/index.ts +3 -0
- package/src/services/session-manager/session-manager.service.ts +405 -0
- package/src/utils/get-user-subscription.ts +84 -0
- package/src/utils/index.ts +1 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Document } from '@langchain/core/documents';
|
|
2
|
+
import { stringifyDocs } from '../stringify-docs.js';
|
|
3
|
+
|
|
4
|
+
describe('stringifyDocs', () => {
|
|
5
|
+
it('should stringify a single document', () => {
|
|
6
|
+
const doc = new Document({
|
|
7
|
+
pageContent: 'Test content',
|
|
8
|
+
metadata: { id: '123' },
|
|
9
|
+
});
|
|
10
|
+
const result = stringifyDocs([doc]);
|
|
11
|
+
expect(result).toBe('#ID:123\n Document: Test content \n \n ');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should stringify multiple documents', () => {
|
|
15
|
+
const docs = [
|
|
16
|
+
new Document({
|
|
17
|
+
pageContent: 'First content',
|
|
18
|
+
metadata: { id: '1' },
|
|
19
|
+
}),
|
|
20
|
+
new Document({
|
|
21
|
+
pageContent: 'Second content',
|
|
22
|
+
metadata: { id: '2' },
|
|
23
|
+
}),
|
|
24
|
+
];
|
|
25
|
+
const expected =
|
|
26
|
+
'#ID:1\n Document: First content \n \n \n\n#ID:2\n Document: Second content \n \n ';
|
|
27
|
+
expect(stringifyDocs(docs)).toBe(expected);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle empty array', () => {
|
|
31
|
+
expect(stringifyDocs([])).toBe('');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should handle documents with special characters', () => {
|
|
35
|
+
const doc = new Document({
|
|
36
|
+
pageContent: 'Content with \n newline and \t tab',
|
|
37
|
+
metadata: { id: 'special' },
|
|
38
|
+
});
|
|
39
|
+
const expected =
|
|
40
|
+
'#ID:special\n Document: Content with \n newline and \t tab \n \n ';
|
|
41
|
+
expect(stringifyDocs([doc])).toBe(expected);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle documents with empty content', () => {
|
|
45
|
+
const doc = new Document({
|
|
46
|
+
pageContent: '',
|
|
47
|
+
metadata: { id: 'empty' },
|
|
48
|
+
});
|
|
49
|
+
const expected = '#ID:empty\n Document: \n \n ';
|
|
50
|
+
expect(stringifyDocs([doc])).toBe(expected);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should handle documents with missing id in metadata', () => {
|
|
54
|
+
const doc = new Document({
|
|
55
|
+
pageContent: 'Test content',
|
|
56
|
+
metadata: {},
|
|
57
|
+
});
|
|
58
|
+
const expected = '#ID:undefined\n Document: Test content \n \n ';
|
|
59
|
+
expect(stringifyDocs([doc])).toBe(expected);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function chunkArr<T>(array: T[], chunkSize: number): T[][] {
|
|
2
|
+
if (chunkSize <= 0) {
|
|
3
|
+
throw new Error('Chunk size must be greater than 0');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const chunks: T[][] = [];
|
|
7
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
8
|
+
const chunk = array.slice(i, i + chunkSize);
|
|
9
|
+
chunks.push(chunk);
|
|
10
|
+
}
|
|
11
|
+
return chunks;
|
|
12
|
+
}
|
|
13
|
+
export { chunkArr };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Document } from '@langchain/core/documents';
|
|
2
|
+
import { type BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
3
|
+
import { ChatPromptTemplate } from '@langchain/core/prompts';
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
import { getChatOpenAiModel } from '../models/openai.js';
|
|
6
|
+
|
|
7
|
+
type TCheckDocRelevanceArgs = {
|
|
8
|
+
doc: Document | string;
|
|
9
|
+
query: string;
|
|
10
|
+
model?: BaseLanguageModel;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
async function checkDocRelevance({
|
|
14
|
+
doc,
|
|
15
|
+
model = getChatOpenAiModel(),
|
|
16
|
+
query,
|
|
17
|
+
}: TCheckDocRelevanceArgs): Promise<boolean> {
|
|
18
|
+
const prompt = ChatPromptTemplate.fromTemplate(
|
|
19
|
+
`You are an AI agent responsible for determining the relevance of a document to a given query.
|
|
20
|
+
|
|
21
|
+
DOCUMENT: ${typeof doc === 'string' ? doc : doc.pageContent}
|
|
22
|
+
QUERY: ${query}
|
|
23
|
+
|
|
24
|
+
### Acceptance Criteria
|
|
25
|
+
(1) Your goal is to identify DOCUMENTs that are completely unrelated to the QUERY.
|
|
26
|
+
(2) If the DOCUMENT contain any keywords or semantic meaning related to the QUERY, consider them relevant.
|
|
27
|
+
(3) It is acceptable if the DOCUMENT have some information that is unrelated to the QUERY, as long as (2) is met.
|
|
28
|
+
|
|
29
|
+
### Instructions
|
|
30
|
+
(1) Review the DOCUMENT and determine if it is relevant to the QUERY.
|
|
31
|
+
(2) Provide a boolean answer indicating the relevance of the DOCUMENT to the QUERY.
|
|
32
|
+
(3) If the DOCUMENT is relevant, set the answer to true. Otherwise, set the answer to false.
|
|
33
|
+
`,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const zodSchema = z.object({
|
|
37
|
+
answer: z.boolean(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!model) {
|
|
41
|
+
throw new Error('Model is not set');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!model.withStructuredOutput) {
|
|
45
|
+
throw new Error('Model does not support structured output');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const structuredLlm = model.withStructuredOutput(zodSchema);
|
|
49
|
+
|
|
50
|
+
const chain = prompt.pipe(structuredLlm);
|
|
51
|
+
const response = await chain.invoke({
|
|
52
|
+
query,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return response.answer;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default checkDocRelevance;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Document } from '@langchain/core/documents';
|
|
2
|
+
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
|
|
3
|
+
|
|
4
|
+
import z from 'zod';
|
|
5
|
+
|
|
6
|
+
const splitter = new RecursiveCharacterTextSplitter();
|
|
7
|
+
|
|
8
|
+
export const docSplitter = (text: string | string[]): Promise<Document[]> => {
|
|
9
|
+
if (!text) throw new Error('No text provided');
|
|
10
|
+
|
|
11
|
+
const _text = typeof text === 'string' ? [text] : text;
|
|
12
|
+
arrSchema.parse(_text);
|
|
13
|
+
|
|
14
|
+
return splitter.createDocuments(_text);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const arrSchema = z
|
|
18
|
+
.array(
|
|
19
|
+
z.string({
|
|
20
|
+
message: 'Invalid text provided',
|
|
21
|
+
}),
|
|
22
|
+
{
|
|
23
|
+
message: 'Invalid text provided in array',
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
.nonempty({
|
|
27
|
+
message: 'Text array cannot be empty',
|
|
28
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Document } from '@langchain/core/documents';
|
|
2
|
+
|
|
3
|
+
function filterSimilaritySearchResults<
|
|
4
|
+
Doc extends Document,
|
|
5
|
+
Result extends [Doc, number][],
|
|
6
|
+
>(results: Result, threshold: number): Doc[] {
|
|
7
|
+
const filteredResults = [];
|
|
8
|
+
for (const [doc, score] of results) {
|
|
9
|
+
if (score >= threshold) {
|
|
10
|
+
filteredResults.push(doc);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return filteredResults;
|
|
14
|
+
}
|
|
15
|
+
export { filterSimilaritySearchResults };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Document } from 'langchain';
|
|
2
|
+
import { getOpenAiClient } from '../models/openai.js';
|
|
3
|
+
|
|
4
|
+
export const generateQuestionsFromChunks = async (
|
|
5
|
+
content: string,
|
|
6
|
+
chunks: Document[],
|
|
7
|
+
): Promise<QuestionGeneratorOutput['json_output']> => {
|
|
8
|
+
const openai = getOpenAiClient();
|
|
9
|
+
const response = await openai.chat.completions.parse({
|
|
10
|
+
model: 'gpt-4o-mini',
|
|
11
|
+
messages: [
|
|
12
|
+
{
|
|
13
|
+
role: 'system',
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: '# SYSTEM — “Question Generator with Visible Chain-of-Thought”\n\nYou are an expert dataset-builder. \nYour mission: turn raw knowledge into high-quality Q-A pairs for semantic search **while showing your reasoning**.\n\n---\n## INPUTS\n- **chunks** – array → `{ "id": string, "text": string }` \n- **fullText** – the complete source document.\n\n---\n## TASK\nFor **each** chunk:\n\n1. **THINK (visible)** \n • Analyse the chunk’s content. \n • Note key facts, entities, dates, numbers, causal links, etc. \n • Decide which facts yield **Direct** vs **Derived** questions. \n • *Keep thoughts ≤ 120 words.*\n\n2. **GENERATE** \n • **Direct Q-A** – 1-3 questions whose full answer text appears **verbatim (or nearly so)** in the *same chunk*. \n • **Derived Q-A** – 1-2 questions that require light inference or synthesis; answers may use the chunk **or** *fullText*. \n • Ensure **at least two distinct sections** of the chunk are represented among the questions.\n\n---\n## OUTPUT FORMAT \nReturn **one valid JSON object** that matches the schema below. \nOutput **nothing** else.\n\n```json\n{\n "thoughts": "<your chain-of-thought here>",\n "json_output": [\n {\n "chunkId": "...",\n "qas": [\n { "type": "direct", "question": "...?", "answer": "..." },\n { "type": "derived", "question": "...?", "answer": "..." }\n // …\n ]\n }\n // …more chunks\n ]\n}\n',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
role: 'user',
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
text: `## CHUNKS
|
|
27
|
+
${chunks
|
|
28
|
+
.map((chunk) => {
|
|
29
|
+
return `### CHUNK ${chunk.id}
|
|
30
|
+
${chunk.pageContent}`;
|
|
31
|
+
})
|
|
32
|
+
.join('\n')}
|
|
33
|
+
## FULL TEXT
|
|
34
|
+
${content}`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
response_format: {
|
|
40
|
+
type: 'json_schema',
|
|
41
|
+
json_schema: {
|
|
42
|
+
name: 'question_generator_output',
|
|
43
|
+
strict: true,
|
|
44
|
+
schema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
thoughts: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Visible chain-of-thought reasoning.',
|
|
50
|
+
},
|
|
51
|
+
json_output: {
|
|
52
|
+
type: 'array',
|
|
53
|
+
description: 'Array of chunks with their generated Q&A pairs.',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
chunkId: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'Identifier of the chunk.',
|
|
60
|
+
},
|
|
61
|
+
qas: {
|
|
62
|
+
type: 'array',
|
|
63
|
+
items: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
type: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
enum: ['direct', 'derived'],
|
|
69
|
+
description:
|
|
70
|
+
'Question type: direct (verbatim) or derived (inference).',
|
|
71
|
+
},
|
|
72
|
+
question: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'The question text.',
|
|
75
|
+
},
|
|
76
|
+
answer: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'The answer text.',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ['type', 'question', 'answer'],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ['chunkId', 'qas'],
|
|
87
|
+
additionalProperties: false,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
required: ['thoughts', 'json_output'],
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
temperature: 0,
|
|
97
|
+
max_completion_tokens: 9957,
|
|
98
|
+
top_p: 0.28,
|
|
99
|
+
frequency_penalty: 0,
|
|
100
|
+
presence_penalty: 0,
|
|
101
|
+
store: false,
|
|
102
|
+
});
|
|
103
|
+
const json = response.choices[0]?.message
|
|
104
|
+
.parsed as unknown as QuestionGeneratorOutput;
|
|
105
|
+
return json.json_output;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type QuestionGeneratorOutput = {
|
|
109
|
+
thoughts: string;
|
|
110
|
+
json_output: {
|
|
111
|
+
chunkId: string;
|
|
112
|
+
qas: { type: string; question: string; answer: string }[];
|
|
113
|
+
}[];
|
|
114
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './chunk-arr.js';
|
|
2
|
+
export * from './doc-relevance-checker.js';
|
|
3
|
+
export * from './doc-splitter.js';
|
|
4
|
+
export * from './filter-similarity-search-results.js';
|
|
5
|
+
export * from './generate-questions-from-chunks.js';
|
|
6
|
+
export * from './json-to-yaml.js';
|
|
7
|
+
export * from './load-file.js';
|
|
8
|
+
export * from './stringify-docs.js';
|
|
9
|
+
export * from './transformGraphStateMessageToListMessageResponse.js';
|
|
10
|
+
export * from './verify-matrix-openId-token.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const jsonToYaml = <T extends object>(
|
|
2
|
+
json: T,
|
|
3
|
+
indentLevel = 0,
|
|
4
|
+
): string => {
|
|
5
|
+
const indent = ' '.repeat(indentLevel); // Two spaces per level of indentation
|
|
6
|
+
|
|
7
|
+
const isArray = Array.isArray(json);
|
|
8
|
+
|
|
9
|
+
if (isArray) {
|
|
10
|
+
return (json as unknown as any[])
|
|
11
|
+
.map((item) => {
|
|
12
|
+
if (item !== null && typeof item === 'object') {
|
|
13
|
+
// Recursively process nested objects/arrays with increased indentation
|
|
14
|
+
const nestedYaml = jsonToYaml(item, indentLevel + 1);
|
|
15
|
+
return `${indent}- \n${nestedYaml}`;
|
|
16
|
+
}
|
|
17
|
+
return `${indent}- ${item}`;
|
|
18
|
+
})
|
|
19
|
+
.join('\n');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return Object.entries(json)
|
|
23
|
+
.map(([key, value]) => {
|
|
24
|
+
if (value !== null && typeof value === 'object') {
|
|
25
|
+
// Recursively process nested objects with increased indentation
|
|
26
|
+
const nestedYaml = jsonToYaml(value, indentLevel + 1);
|
|
27
|
+
return `${indent}${key}:\n${nestedYaml}`;
|
|
28
|
+
}
|
|
29
|
+
return `${indent}${key}: ${value}`;
|
|
30
|
+
})
|
|
31
|
+
.join('\n');
|
|
32
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Logger } from '@ixo/logger';
|
|
2
|
+
import { DocxLoader } from '@langchain/community/document_loaders/fs/docx';
|
|
3
|
+
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';
|
|
4
|
+
import { HtmlToTextTransformer } from '@langchain/community/document_transformers/html_to_text';
|
|
5
|
+
import { Document } from '@langchain/core/documents';
|
|
6
|
+
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
|
|
9
|
+
type SupportedFileType = 'pdf' | 'markdown' | 'html' | 'text' | 'doc';
|
|
10
|
+
const splitter = new RecursiveCharacterTextSplitter({
|
|
11
|
+
chunkSize: 1000,
|
|
12
|
+
chunkOverlap: 200,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const determineFileType = (
|
|
16
|
+
path: string,
|
|
17
|
+
contentType?: string,
|
|
18
|
+
): SupportedFileType | null => {
|
|
19
|
+
const extension = path.split('.').pop()?.toLowerCase();
|
|
20
|
+
|
|
21
|
+
// Determine based on file extension
|
|
22
|
+
if (extension) {
|
|
23
|
+
if (['pdf'].includes(extension)) return 'pdf';
|
|
24
|
+
if (['md', 'markdown'].includes(extension)) return 'markdown';
|
|
25
|
+
if (['html', 'htm'].includes(extension)) return 'html';
|
|
26
|
+
if (['txt'].includes(extension)) return 'text';
|
|
27
|
+
if (['doc', 'docx'].includes(extension)) return 'doc';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Determine based on MIME type
|
|
31
|
+
if (contentType) {
|
|
32
|
+
if (contentType.includes('application/pdf')) return 'pdf';
|
|
33
|
+
if (contentType.includes('text/markdown')) return 'markdown';
|
|
34
|
+
if (contentType.includes('text/html')) return 'html';
|
|
35
|
+
if (contentType.includes('text/plain')) return 'text';
|
|
36
|
+
if (
|
|
37
|
+
contentType.includes('application/msword') ||
|
|
38
|
+
contentType.includes(
|
|
39
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
40
|
+
)
|
|
41
|
+
) {
|
|
42
|
+
return 'doc';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Loads a file (PDF, Markdown, HTML, Text, DOC) from a given path or URL and returns processed content.
|
|
51
|
+
*
|
|
52
|
+
* @param path - The file path or URL of the document to load.
|
|
53
|
+
* @param fetchOptions - Optional fetch options (e.g., headers for authorization).
|
|
54
|
+
* @returns A promise that resolves to an array of Document objects.
|
|
55
|
+
* @throws If the file type is unsupported or the file cannot be processed.
|
|
56
|
+
*/
|
|
57
|
+
export const loadFile = async (
|
|
58
|
+
path: string,
|
|
59
|
+
fetchOptions?: RequestInit,
|
|
60
|
+
): Promise<Document[]> => {
|
|
61
|
+
const isUrl = path.startsWith('http');
|
|
62
|
+
|
|
63
|
+
if (isUrl) {
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(path, fetchOptions);
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const error = await getErrorFromResponse(response);
|
|
69
|
+
// eslint-disable-next-line no-console -- debug
|
|
70
|
+
console.error(`Error fetching file from URL: ${path}`, error);
|
|
71
|
+
throw new Error(`Failed to fetch file from URL: ${path}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
75
|
+
const fileType = determineFileType(path, contentType);
|
|
76
|
+
|
|
77
|
+
if (!fileType) {
|
|
78
|
+
throw new Error(`Unsupported file type. Content-Type: ${contentType}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const data = await response.arrayBuffer();
|
|
82
|
+
const blob = new Blob([data], {
|
|
83
|
+
type: contentType || 'application/octet-stream',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return processFile(blob, fileType);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
Logger.error(`Failed to load file from URL: ${path}`, error);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const buffer = await fs.readFile(path);
|
|
95
|
+
const blob = new Blob([buffer]);
|
|
96
|
+
|
|
97
|
+
const fileType = determineFileType(path);
|
|
98
|
+
|
|
99
|
+
if (!fileType) {
|
|
100
|
+
throw new Error(`Unsupported file type for file: ${path}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return processFile(blob, fileType);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
Logger.error(`Failed to load local file from path: ${path}`, error);
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const processFile = async (
|
|
111
|
+
blob: Blob,
|
|
112
|
+
fileType: SupportedFileType,
|
|
113
|
+
): Promise<Document[]> => {
|
|
114
|
+
switch (fileType) {
|
|
115
|
+
case 'pdf': {
|
|
116
|
+
const pdfLoader = new PDFLoader(blob);
|
|
117
|
+
return pdfLoader.load();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
case 'markdown': {
|
|
121
|
+
return loadMarkdown(blob);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case 'html': {
|
|
125
|
+
const html = await blobToString(blob);
|
|
126
|
+
const htmlSplitter = RecursiveCharacterTextSplitter.fromLanguage('html');
|
|
127
|
+
const transformer = new HtmlToTextTransformer();
|
|
128
|
+
const sequence = htmlSplitter.pipe(transformer);
|
|
129
|
+
const docs = await sequence.invoke([
|
|
130
|
+
new Document({
|
|
131
|
+
pageContent: html,
|
|
132
|
+
}),
|
|
133
|
+
]);
|
|
134
|
+
return docs;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
case 'text':
|
|
138
|
+
return splitter.createDocuments([await blobToString(blob)]);
|
|
139
|
+
|
|
140
|
+
case 'doc': {
|
|
141
|
+
const docxLoader = new DocxLoader(blob);
|
|
142
|
+
return docxLoader.load();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
default:
|
|
146
|
+
throw new Error(`Unsupported file type: ${fileType as string}`);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const loadMarkdown = async (blob: Blob): Promise<Document[]> => {
|
|
151
|
+
const markdown = await blobToString(blob);
|
|
152
|
+
return splitter.createDocuments([markdown]);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const blobToString = async (blob: Blob): Promise<string> => {
|
|
156
|
+
const buffer = await blob.arrayBuffer(); // Convert Blob to ArrayBuffer
|
|
157
|
+
return Buffer.from(buffer).toString('utf-8'); // Convert ArrayBuffer to string
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const getErrorFromResponse = async (response: Response): Promise<unknown> => {
|
|
161
|
+
try {
|
|
162
|
+
const json = await response.json();
|
|
163
|
+
|
|
164
|
+
return json;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
if (response.bodyUsed) return 'Unknown error';
|
|
167
|
+
const errorMessage = await response.text();
|
|
168
|
+
return errorMessage;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Document } from '@langchain/core/documents';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param docs - Array of documents to stringify
|
|
6
|
+
* @returns - Stringified documents with ID in Header
|
|
7
|
+
*/
|
|
8
|
+
export const stringifyDocs = (docs: Document[]): string => {
|
|
9
|
+
return docs
|
|
10
|
+
.map(
|
|
11
|
+
(doc) => `#ID:${doc.metadata.id}\n Document: ${doc.pageContent} \n \n `,
|
|
12
|
+
)
|
|
13
|
+
.join('\n\n');
|
|
14
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ToolMessage,
|
|
3
|
+
type AIMessage,
|
|
4
|
+
type BaseMessage,
|
|
5
|
+
} from '@langchain/core/messages';
|
|
6
|
+
import { isUUID } from 'class-validator';
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
|
|
9
|
+
interface ToolCall {
|
|
10
|
+
name: string;
|
|
11
|
+
id: string;
|
|
12
|
+
args: unknown;
|
|
13
|
+
status?: 'isRunning' | 'done';
|
|
14
|
+
output?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface MessageDto {
|
|
18
|
+
id: string;
|
|
19
|
+
type: 'ai' | 'human';
|
|
20
|
+
content: string;
|
|
21
|
+
toolCalls?: ToolCall[];
|
|
22
|
+
reasoning?: string;
|
|
23
|
+
isComplete?: boolean;
|
|
24
|
+
isReasoning?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ListOracleMessagesResponse {
|
|
28
|
+
messages: MessageDto[];
|
|
29
|
+
}
|
|
30
|
+
export interface CleanAdditionalKwargs {
|
|
31
|
+
msgFromMatrixRoom: boolean;
|
|
32
|
+
timestamp: string;
|
|
33
|
+
oracleName: string;
|
|
34
|
+
reasoning?: string;
|
|
35
|
+
reasoningDetails?: Array<{
|
|
36
|
+
type: string;
|
|
37
|
+
text: string;
|
|
38
|
+
}>;
|
|
39
|
+
[key: string]: unknown; // Allow additional properties for LangChain compatibility
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function transformGraphStateMessageToListMessageResponse(
|
|
43
|
+
messages: BaseMessage[],
|
|
44
|
+
): ListOracleMessagesResponse {
|
|
45
|
+
return {
|
|
46
|
+
messages: messages.reduce<MessageDto[]>((acc, message) => {
|
|
47
|
+
const toolMsg = message.type === 'tool' ? (message as ToolMessage) : null;
|
|
48
|
+
if (message.type !== 'system' && message.type !== 'tool' && !message.additional_kwargs?.isError) {
|
|
49
|
+
// Extract reasoning from additional_kwargs
|
|
50
|
+
const additionalKwargs =
|
|
51
|
+
message.additional_kwargs as CleanAdditionalKwargs;
|
|
52
|
+
const reasoning = additionalKwargs?.reasoning;
|
|
53
|
+
|
|
54
|
+
acc.push({
|
|
55
|
+
type: message.type === 'ai' ? 'ai' : 'human',
|
|
56
|
+
content: message.content.toString(),
|
|
57
|
+
id: uuidFromString(message.id ?? message.content.toString()),
|
|
58
|
+
toolCalls: (message as AIMessage).tool_calls?.map((toolCall) => ({
|
|
59
|
+
name: toolCall.name,
|
|
60
|
+
args: toolCall.args,
|
|
61
|
+
id: toolCall.id ?? uuidFromString(JSON.stringify(toolCall.args)),
|
|
62
|
+
output: undefined,
|
|
63
|
+
})),
|
|
64
|
+
reasoning,
|
|
65
|
+
isComplete: true, // Messages from DB are always complete
|
|
66
|
+
isReasoning: false, // since this is not a reasoning message and the request is done
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (toolMsg) {
|
|
70
|
+
const toolCallId =
|
|
71
|
+
toolMsg.lc_kwargs.tool_call_id ??
|
|
72
|
+
uuidFromString(JSON.stringify(toolMsg.lc_kwargs.args));
|
|
73
|
+
const messageWithToolCallIdIdx = acc.findIndex((m) =>
|
|
74
|
+
m.toolCalls?.find((t) => t.id === toolCallId),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// if the message with the tool call id exits then update the tool Call to add the output
|
|
78
|
+
const el =
|
|
79
|
+
messageWithToolCallIdIdx !== -1
|
|
80
|
+
? acc[messageWithToolCallIdIdx]
|
|
81
|
+
: null;
|
|
82
|
+
if (el) {
|
|
83
|
+
el.toolCalls = el.toolCalls?.map((t) =>
|
|
84
|
+
t.id === toolCallId
|
|
85
|
+
? {
|
|
86
|
+
...t,
|
|
87
|
+
output: JSON.stringify(toolMsg.content),
|
|
88
|
+
status: 'done',
|
|
89
|
+
}
|
|
90
|
+
: t,
|
|
91
|
+
);
|
|
92
|
+
acc[messageWithToolCallIdIdx] = el;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return acc;
|
|
97
|
+
}, []),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const uuidFromString = (str: string): string => {
|
|
102
|
+
const isStrUUID = isUUID(str);
|
|
103
|
+
if (isStrUUID) return str;
|
|
104
|
+
// generate a uuid from a string
|
|
105
|
+
const hash = crypto.createHash('sha256');
|
|
106
|
+
hash.update(str);
|
|
107
|
+
return hash.digest('hex');
|
|
108
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Logger } from "@ixo/logger";
|
|
2
|
+
|
|
3
|
+
const ERROR_CODES = ['M_UNKNOWN_TOKEN', 'M_FORBIDDEN'];
|
|
4
|
+
export async function verifyMatrixOpenIdToken(
|
|
5
|
+
openIdToken: string,
|
|
6
|
+
baseUrl: string,
|
|
7
|
+
): Promise<{ isValid: boolean; userId?: string; error?: string }> {
|
|
8
|
+
try {
|
|
9
|
+
// Make request to Matrix federation endpoint
|
|
10
|
+
const response = await fetch(
|
|
11
|
+
`${baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl}/_matrix/federation/v1/openid/userinfo?access_token=${openIdToken}`,
|
|
12
|
+
{
|
|
13
|
+
method: 'GET',
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (response.ok) {
|
|
18
|
+
const userInfo = (await response.json()) as { sub: string };
|
|
19
|
+
console.log('User info from server:', userInfo);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
isValid: true,
|
|
23
|
+
userId: userInfo.sub, // The verified user ID
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const error = await response.json() as { errcode: string };
|
|
27
|
+
const isAuthError = ERROR_CODES.includes(error.errcode);
|
|
28
|
+
if (!isAuthError) {
|
|
29
|
+
Logger.error('Error verifying Matrix OpenID token:', error);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
isValid: false,
|
|
33
|
+
error: `Server returned ${response.status}: ${response.statusText}`,
|
|
34
|
+
};
|
|
35
|
+
} catch (error) {
|
|
36
|
+
const errorMessage =
|
|
37
|
+
error instanceof Error ? error.message : String(error);
|
|
38
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
39
|
+
|
|
40
|
+
console.error('Error verifying Matrix OpenID token:', errorMessage, errorStack);
|
|
41
|
+
return {
|
|
42
|
+
isValid: false,
|
|
43
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.ts
ADDED