doc2vec 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/Dockerfile +8 -17
  2. package/Dockerfile.old +19 -0
  3. package/README.md +21 -0
  4. package/config.yaml.new +239 -165
  5. package/config.yaml.ok +164 -0
  6. package/config.yaml.solo +251 -0
  7. package/dist/doc2vec.js +160 -84
  8. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/acm-cdk-stack.js +20 -0
  9. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/route53-cdk-stack.js +33 -0
  10. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/lib/waf-cdk-stack.js +66 -0
  11. package/dist/solo-reference-architectures/Gloo_Edge/ALB_Last_Mile_Encryption/data/acm-waf-cdk/test/s3-cloudfront-cdk.test.js +16 -0
  12. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/lib/acm-cdk-stack.js +21 -0
  13. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/lib/s3-cloudfront-cdk-stack.js +95 -0
  14. package/dist/solo-reference-architectures/Gloo_Edge/Cloudfront_NLB/data/s3-cloudfront-cdk/test/s3-cloudfront-cdk.test.js +16 -0
  15. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/bin/acm-cdk.js +55 -0
  16. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/lib/acm-cdk-stack.js +20 -0
  17. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/lib/route53-cdk-stack.js +33 -0
  18. package/dist/solo-reference-architectures/Gloo_Gateway/aws-nlb-tls-offloading/data/acm-cdk/test/s3-cloudfront-cdk.test.js +16 -0
  19. package/doc2vec.ts +184 -93
  20. package/doc2vec.ts.ok +1730 -0
  21. package/package.json +4 -2
  22. package/tmp/README.md.old +1 -0
  23. package/tmp.db +0 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AcmCdkStack = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ class AcmCdkStack extends aws_cdk_lib_1.Stack {
6
+ constructor(scope, id, props) {
7
+ super(scope, id, props);
8
+ if (!process.env.DOMAIN_NAME) {
9
+ throw new Error("DOMAIN_NAME environment variable is not defined");
10
+ }
11
+ const edgeSiteDomainName = "edge." + process.env.DOMAIN_NAME;
12
+ const zone = aws_cdk_lib_1.aws_route53.HostedZone.fromLookup(this, 'Zone', { domainName: process.env.DOMAIN_NAME });
13
+ // setup acm
14
+ this.acmCertificate = new aws_cdk_lib_1.aws_certificatemanager.Certificate(this, 'SiteCertificate', {
15
+ domainName: edgeSiteDomainName,
16
+ validation: aws_cdk_lib_1.aws_certificatemanager.CertificateValidation.fromDns(zone),
17
+ });
18
+ new aws_cdk_lib_1.CfnOutput(this, 'ACMCertificate', { value: this.acmCertificate.certificateArn });
19
+ }
20
+ }
21
+ exports.AcmCdkStack = AcmCdkStack;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.S3CloudfrontCdkStack = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ class S3CloudfrontCdkStack extends aws_cdk_lib_1.Stack {
6
+ constructor(scope, id, props) {
7
+ super(scope, id, props);
8
+ const acmCertificate = props.acmCertificate;
9
+ const originBucketUriHeader = 'x-s3-bucket-host';
10
+ const bucketName = process.env.S3_BUCKET_NAME || "default-s3-bucket";
11
+ const bucketUri = process.env.S3_BUCKET_URI;
12
+ if (!process.env.DOMAIN_NAME) {
13
+ throw new Error("DOMAIN_NAME environment variable is not defined");
14
+ }
15
+ const edgeSiteDomainName = "edge." + process.env.DOMAIN_NAME;
16
+ const originDomainName = "apps." + process.env.DOMAIN_NAME;
17
+ if (!bucketUri) {
18
+ throw new Error("S3_BUCKET_URI environment variable is not defined");
19
+ }
20
+ const zone = aws_cdk_lib_1.aws_route53.HostedZone.fromLookup(this, 'Zone', { domainName: process.env.DOMAIN_NAME });
21
+ // create an S3 bucket
22
+ const bucket = new aws_cdk_lib_1.aws_s3.Bucket(this, 'PrivateBucket', {
23
+ bucketName: bucketName,
24
+ removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
25
+ autoDeleteObjects: true,
26
+ publicReadAccess: false,
27
+ blockPublicAccess: new aws_cdk_lib_1.aws_s3.BlockPublicAccess({
28
+ blockPublicAcls: false,
29
+ blockPublicPolicy: false,
30
+ ignorePublicAcls: false,
31
+ restrictPublicBuckets: false
32
+ }),
33
+ versioned: true,
34
+ });
35
+ // attach the policy to only credentials based on SigV4
36
+ let authHeaderPolicy = new aws_cdk_lib_1.aws_iam.PolicyStatement({
37
+ actions: ['s3:Get*'],
38
+ resources: [bucket.arnForObjects('*')],
39
+ principals: [new aws_cdk_lib_1.aws_iam.AnyPrincipal()]
40
+ });
41
+ authHeaderPolicy.addCondition('StringEquals', {
42
+ 's3:signatureversion': 'AWS4-HMAC-SHA256'
43
+ });
44
+ authHeaderPolicy.addCondition('StringEquals', {
45
+ 's3:authType': 'REST-HEADER'
46
+ });
47
+ bucket.addToResourcePolicy(authHeaderPolicy);
48
+ // deploy all the artifacts
49
+ new aws_cdk_lib_1.aws_s3_deployment.BucketDeployment(this, 'DeployToBucket', {
50
+ sources: [aws_cdk_lib_1.aws_s3_deployment.Source.asset('./s3-bucket-contents/original')],
51
+ destinationBucket: bucket,
52
+ });
53
+ // setup cloudfront
54
+ const cdnDistribution = new aws_cdk_lib_1.aws_cloudfront.Distribution(this, 'SiteCDN', {
55
+ domainNames: [edgeSiteDomainName],
56
+ certificate: acmCertificate,
57
+ minimumProtocolVersion: aws_cdk_lib_1.aws_cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021,
58
+ enableIpv6: false,
59
+ defaultBehavior: {
60
+ origin: new aws_cdk_lib_1.aws_cloudfront_origins.HttpOrigin(originDomainName, {
61
+ protocolPolicy: aws_cdk_lib_1.aws_cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
62
+ originSslProtocols: [aws_cdk_lib_1.aws_cloudfront.OriginSslPolicy.TLS_V1_2],
63
+ httpsPort: 443,
64
+ readTimeout: aws_cdk_lib_1.Duration.seconds(45),
65
+ connectionTimeout: aws_cdk_lib_1.Duration.seconds(10),
66
+ customHeaders: {
67
+ 'x-s3-bucket-host': bucketUri
68
+ }
69
+ }),
70
+ viewerProtocolPolicy: aws_cdk_lib_1.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
71
+ allowedMethods: aws_cdk_lib_1.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
72
+ cachedMethods: aws_cdk_lib_1.aws_cloudfront.CachedMethods.CACHE_GET_HEAD_OPTIONS,
73
+ cachePolicy: aws_cdk_lib_1.aws_cloudfront.CachePolicy.CACHING_OPTIMIZED,
74
+ compress: true
75
+ }
76
+ });
77
+ // add A record to route53
78
+ new aws_cdk_lib_1.aws_route53.ARecord(this, 'SiteAliasARecord', {
79
+ recordName: edgeSiteDomainName,
80
+ target: aws_cdk_lib_1.aws_route53.RecordTarget.fromAlias(new aws_cdk_lib_1.aws_route53_targets.CloudFrontTarget(cdnDistribution)),
81
+ zone
82
+ });
83
+ // output information
84
+ new aws_cdk_lib_1.CfnOutput(this, 'SiteDistribution', {
85
+ value: cdnDistribution.distributionId,
86
+ });
87
+ new aws_cdk_lib_1.CfnOutput(this, 'BucketDomain', {
88
+ value: bucket.bucketRegionalDomainName,
89
+ });
90
+ new aws_cdk_lib_1.CfnOutput(this, 'SiteDomain', {
91
+ value: cdnDistribution.domainName,
92
+ });
93
+ }
94
+ }
95
+ exports.S3CloudfrontCdkStack = S3CloudfrontCdkStack;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // import * as cdk from 'aws-cdk-lib';
3
+ // import { Template } from 'aws-cdk-lib/assertions';
4
+ // import * as S3CloudfrontCdk from '../lib/s3-cloudfront-cdk-stack';
5
+ // example test. To run these tests, uncomment this file along with the
6
+ // example resource in lib/s3-cloudfront-cdk-stack.ts
7
+ test('SQS Queue Created', () => {
8
+ // const app = new cdk.App();
9
+ // // WHEN
10
+ // const stack = new S3CloudfrontCdk.S3CloudfrontCdkStack(app, 'MyTestStack');
11
+ // // THEN
12
+ // const template = Template.fromStack(stack);
13
+ // template.hasResourceProperties('AWS::SQS::Queue', {
14
+ // VisibilityTimeout: 300
15
+ // });
16
+ });
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ require("source-map-support/register");
38
+ const cdk = __importStar(require("aws-cdk-lib"));
39
+ const acm_cdk_stack_1 = require("../lib/acm-cdk-stack");
40
+ const route53_cdk_stack_1 = require("../lib/route53-cdk-stack");
41
+ const app = new cdk.App();
42
+ const route53Stack = new route53_cdk_stack_1.Route53CdkStack(app, 'Route53CdkStack', {
43
+ env: {
44
+ account: process.env.CDK_DEFAULT_ACCOUNT,
45
+ region: process.env.CLUSTER_REGION || process.env.CDK_DEFAULT_REGION
46
+ }
47
+ });
48
+ const acmStack = new acm_cdk_stack_1.ACMCdkStack(app, 'ACMCdkStack', {
49
+ env: {
50
+ account: process.env.CDK_DEFAULT_ACCOUNT,
51
+ region: process.env.CLUSTER_REGION || process.env.CDK_DEFAULT_REGION
52
+ },
53
+ hostedZone: route53Stack.hostedZone
54
+ });
55
+ acmStack.addDependency(route53Stack);
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ACMCdkStack = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ class ACMCdkStack extends aws_cdk_lib_1.Stack {
6
+ constructor(scope, id, props) {
7
+ super(scope, id, props);
8
+ if (!process.env.DOMAIN_NAME) {
9
+ throw new Error("DOMAIN_NAME environment variable is not defined");
10
+ }
11
+ const hostedZone = props.hostedZone;
12
+ const edgeSiteDomainName = "apps." + process.env.DOMAIN_NAME;
13
+ this.acmCertificate = new aws_cdk_lib_1.aws_certificatemanager.Certificate(this, 'SiteCertificate', {
14
+ domainName: edgeSiteDomainName,
15
+ validation: aws_cdk_lib_1.aws_certificatemanager.CertificateValidation.fromDns(hostedZone),
16
+ });
17
+ new aws_cdk_lib_1.CfnOutput(this, 'ACMCertificate', { value: this.acmCertificate.certificateArn });
18
+ }
19
+ }
20
+ exports.ACMCdkStack = ACMCdkStack;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Route53CdkStack = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ class Route53CdkStack extends aws_cdk_lib_1.Stack {
6
+ constructor(scope, id, props) {
7
+ super(scope, id, props);
8
+ if (!process.env.PARENT_DOMAIN_NAME) {
9
+ throw new Error("PARENT_DOMAIN_NAME environment variable is not defined");
10
+ }
11
+ if (!process.env.DOMAIN_NAME) {
12
+ throw new Error("DOMAIN_NAME environment variable is not defined");
13
+ }
14
+ this.hostedZone = new aws_cdk_lib_1.aws_route53.PublicHostedZone(this, "HostedZone", {
15
+ zoneName: process.env.DOMAIN_NAME,
16
+ comment: "Managed by CDK"
17
+ });
18
+ const parent = aws_cdk_lib_1.aws_route53.HostedZone.fromLookup(this, "ParentHostedZone", {
19
+ domainName: process.env.PARENT_DOMAIN_NAME + "."
20
+ });
21
+ if (!this.hostedZone.hostedZoneNameServers) {
22
+ throw new Error("NS record is undefined.");
23
+ }
24
+ new aws_cdk_lib_1.aws_route53.ZoneDelegationRecord(this, "NS", {
25
+ zone: parent,
26
+ recordName: process.env.DOMAIN_NAME,
27
+ nameServers: this.hostedZone.hostedZoneNameServers,
28
+ comment: "Managed by CDK"
29
+ });
30
+ new aws_cdk_lib_1.CfnOutput(this, 'ZoneId', { value: this.hostedZone.hostedZoneId });
31
+ }
32
+ }
33
+ exports.Route53CdkStack = Route53CdkStack;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // import * as cdk from 'aws-cdk-lib';
3
+ // import { Template } from 'aws-cdk-lib/assertions';
4
+ // import * as S3CloudfrontCdk from '../lib/s3-cloudfront-cdk-stack';
5
+ // example test. To run these tests, uncomment this file along with the
6
+ // example resource in lib/s3-cloudfront-cdk-stack.ts
7
+ test('SQS Queue Created', () => {
8
+ // const app = new cdk.App();
9
+ // // WHEN
10
+ // const stack = new S3CloudfrontCdk.S3CloudfrontCdkStack(app, 'MyTestStack');
11
+ // // THEN
12
+ // const template = Template.fromStack(stack);
13
+ // template.hasResourceProperties('AWS::SQS::Queue', {
14
+ // VisibilityTimeout: 300
15
+ // });
16
+ });
package/doc2vec.ts CHANGED
@@ -43,6 +43,7 @@ interface LocalDirectorySourceConfig extends BaseSourceConfig {
43
43
  exclude_extensions?: string[]; // File extensions to exclude
44
44
  recursive?: boolean; // Whether to traverse subdirectories
45
45
  encoding?: BufferEncoding; // File encoding (default: 'utf8')
46
+ url_rewrite_prefix?: string; // Optional URL prefix to rewrite file:// URLs (e.g., 'https://mydomain.com')
46
47
  }
47
48
 
48
49
  // Configuration specific to website sources
@@ -725,8 +726,33 @@ class Doc2Vec {
725
726
 
726
727
  logger.info(`Processing content from ${filePath} (${content.length} chars)`);
727
728
  try {
728
- // Use the file path as URL for tracking
729
- const fileUrl = `file://${filePath}`;
729
+ // Generate URL based on configuration
730
+ let fileUrl: string;
731
+
732
+ if (config.url_rewrite_prefix) {
733
+ // Replace local path with URL prefix
734
+ const relativePath = path.relative(config.path, filePath).replace(/\\/g, '/');
735
+
736
+ // If relativePath starts with '..', it means the file is outside the base directory
737
+ if (relativePath.startsWith('..')) {
738
+ // For files outside the configured path, use the default file:// scheme
739
+ fileUrl = `file://${filePath}`;
740
+ logger.debug(`File outside configured path, using default URL: ${fileUrl}`);
741
+ } else {
742
+ // For files inside the configured path, rewrite the URL
743
+ // Handle trailing slashes in the URL prefix to avoid double slashes
744
+ const prefix = config.url_rewrite_prefix.endsWith('/')
745
+ ? config.url_rewrite_prefix.slice(0, -1)
746
+ : config.url_rewrite_prefix;
747
+
748
+ fileUrl = `${prefix}/${relativePath}`;
749
+ logger.debug(`URL rewritten: ${filePath} -> ${fileUrl}`);
750
+ }
751
+ } else {
752
+ // Use default file:// URL
753
+ fileUrl = `file://${filePath}`;
754
+ }
755
+
730
756
  const chunks = await this.chunkMarkdown(content, config, fileUrl);
731
757
  logger.info(`Created ${chunks.length} chunks`);
732
758
 
@@ -806,15 +832,13 @@ class Doc2Vec {
806
832
  logger
807
833
  );
808
834
 
809
- logger.info(`Found ${validChunkIds.size} valid chunks across processed files for ${config.path}`);
810
-
811
835
  logger.section('CLEANUP');
812
836
  if (dbConnection.type === 'sqlite') {
813
837
  logger.info(`Running SQLite cleanup for local directory ${config.path}`);
814
- this.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, config.path, logger);
838
+ this.removeObsoleteFilesSQLite(dbConnection.db, processedFiles, config, logger);
815
839
  } else if (dbConnection.type === 'qdrant') {
816
840
  logger.info(`Running Qdrant cleanup for local directory ${config.path} in collection ${dbConnection.collectionName}`);
817
- await this.removeObsoleteFilesQdrant(dbConnection, processedFiles, config.path, logger);
841
+ await this.removeObsoleteFilesQdrant(dbConnection, processedFiles, config, logger);
818
842
  }
819
843
 
820
844
  logger.info(`Finished processing local directory: ${config.path}`);
@@ -921,6 +945,160 @@ class Doc2Vec {
921
945
  }
922
946
  }
923
947
 
948
+ private removeObsoleteFilesSQLite(
949
+ db: Database,
950
+ processedFiles: Set<string>,
951
+ pathConfig: { path: string; url_rewrite_prefix?: string } | string,
952
+ logger: Logger
953
+ ) {
954
+ const getChunksForPathStmt = db.prepare(`
955
+ SELECT chunk_id, url FROM vec_items
956
+ WHERE url LIKE ? || '%'
957
+ `);
958
+ const deleteChunkStmt = db.prepare(`DELETE FROM vec_items WHERE chunk_id = ?`);
959
+
960
+ // Determine if we're using URL rewriting or direct file paths
961
+ const isRewriteMode = typeof pathConfig === 'object' && pathConfig.url_rewrite_prefix;
962
+
963
+ // Set up the URL prefix for searching
964
+ let urlPrefix: string;
965
+ if (isRewriteMode) {
966
+ // Handle URL rewriting case
967
+ urlPrefix = (pathConfig as { path: string; url_rewrite_prefix?: string }).url_rewrite_prefix || '';
968
+ urlPrefix = urlPrefix.endsWith('/') ? urlPrefix.slice(0, -1) : urlPrefix;
969
+ } else {
970
+ // Handle direct file path case
971
+ const dirPrefix = typeof pathConfig === 'string' ? pathConfig : pathConfig.path;
972
+ const cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
973
+ urlPrefix = `file://${cleanedDirPrefix}`;
974
+ }
975
+
976
+ logger.debug(`Searching for chunks with URL prefix: ${urlPrefix}`);
977
+ const existingChunks = getChunksForPathStmt.all(urlPrefix) as { chunk_id: string; url: string }[];
978
+ let deletedCount = 0;
979
+
980
+ const transaction = db.transaction(() => {
981
+ for (const { chunk_id, url } of existingChunks) {
982
+ // Skip if it's not from our URL prefix (safety check)
983
+ if (!url.startsWith(urlPrefix)) continue;
984
+
985
+ let filePath: string;
986
+ let shouldDelete = false;
987
+
988
+ if (isRewriteMode) {
989
+ // URL rewrite mode: extract relative path and construct full file path
990
+ const config = pathConfig as { path: string; url_rewrite_prefix?: string };
991
+ const relativePath = url.substring(urlPrefix.length + 1); // +1 for the '/'
992
+ filePath = path.join(config.path, relativePath);
993
+ shouldDelete = !processedFiles.has(filePath);
994
+ } else {
995
+ // Direct file path mode: remove file:// prefix to match with processedFiles
996
+ filePath = url.substring(7); // Remove 'file://' prefix
997
+ shouldDelete = !processedFiles.has(filePath);
998
+ }
999
+
1000
+ if (shouldDelete) {
1001
+ logger.debug(`Deleting obsolete chunk from SQLite: ${chunk_id.substring(0, 8)}... (File not processed: ${filePath})`);
1002
+ deleteChunkStmt.run(chunk_id);
1003
+ deletedCount++;
1004
+ }
1005
+ }
1006
+ });
1007
+ transaction();
1008
+
1009
+ logger.info(`Deleted ${deletedCount} obsolete chunks from SQLite for URL prefix ${urlPrefix}`);
1010
+ }
1011
+
1012
+ private async removeObsoleteFilesQdrant(
1013
+ db: QdrantDB,
1014
+ processedFiles: Set<string>,
1015
+ pathConfig: { path: string; url_rewrite_prefix?: string } | string,
1016
+ logger: Logger
1017
+ ) {
1018
+ const { client, collectionName } = db;
1019
+ try {
1020
+ // Determine if we're using URL rewriting or direct file paths
1021
+ const isRewriteMode = typeof pathConfig === 'object' && pathConfig.url_rewrite_prefix;
1022
+
1023
+ // Set up the URL prefix for searching
1024
+ let urlPrefix: string;
1025
+ if (isRewriteMode) {
1026
+ // Handle URL rewriting case
1027
+ urlPrefix = (pathConfig as { path: string; url_rewrite_prefix?: string }).url_rewrite_prefix || '';
1028
+ urlPrefix = urlPrefix.endsWith('/') ? urlPrefix.slice(0, -1) : urlPrefix;
1029
+ } else {
1030
+ // Handle direct file path case
1031
+ const dirPrefix = typeof pathConfig === 'string' ? pathConfig : pathConfig.path;
1032
+ const cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
1033
+ urlPrefix = `file://${cleanedDirPrefix}`;
1034
+ }
1035
+
1036
+ logger.debug(`Checking for obsolete chunks with URL prefix: ${urlPrefix}`);
1037
+ const response = await client.scroll(collectionName, {
1038
+ limit: 10000,
1039
+ with_payload: true,
1040
+ with_vector: false,
1041
+ filter: {
1042
+ must: [
1043
+ {
1044
+ key: "url",
1045
+ match: {
1046
+ text: urlPrefix
1047
+ }
1048
+ }
1049
+ ],
1050
+ must_not: [
1051
+ {
1052
+ key: "is_metadata",
1053
+ match: {
1054
+ value: true
1055
+ }
1056
+ }
1057
+ ]
1058
+ }
1059
+ });
1060
+
1061
+ const obsoletePointIds = response.points
1062
+ .filter((point: any) => {
1063
+ const url = point.payload?.url;
1064
+ // Double check it's not a metadata record
1065
+ if (point.payload?.is_metadata === true) {
1066
+ return false;
1067
+ }
1068
+
1069
+ if (!url || !url.startsWith(urlPrefix)) {
1070
+ return false;
1071
+ }
1072
+
1073
+ let filePath: string;
1074
+
1075
+ if (isRewriteMode) {
1076
+ // URL rewrite mode: extract relative path and construct full file path
1077
+ const config = pathConfig as { path: string; url_rewrite_prefix?: string };
1078
+ const relativePath = url.substring(urlPrefix.length + 1); // +1 for the '/'
1079
+ filePath = path.join(config.path, relativePath);
1080
+ } else {
1081
+ // Direct file path mode: remove file:// prefix to match with processedFiles
1082
+ filePath = url.startsWith('file://') ? url.substring(7) : '';
1083
+ }
1084
+
1085
+ return filePath && !processedFiles.has(filePath);
1086
+ })
1087
+ .map((point: any) => point.id);
1088
+
1089
+ if (obsoletePointIds.length > 0) {
1090
+ await client.delete(collectionName, {
1091
+ points: obsoletePointIds,
1092
+ });
1093
+ logger.info(`Deleted ${obsoletePointIds.length} obsolete chunks from Qdrant for URL prefix ${urlPrefix}`);
1094
+ } else {
1095
+ logger.info(`No obsolete chunks to delete from Qdrant for URL prefix ${urlPrefix}`);
1096
+ }
1097
+ } catch (error) {
1098
+ logger.error(`Error removing obsolete chunks from Qdrant:`, error);
1099
+ }
1100
+ }
1101
+
924
1102
  private async initDatabase(config: SourceConfig, parentLogger: Logger): Promise<DatabaseConnection> {
925
1103
  const logger = parentLogger.child('database');
926
1104
  const dbConfig = config.database_config;
@@ -1116,93 +1294,6 @@ class Doc2Vec {
1116
1294
 
1117
1295
  logger.info(`Deleted ${deletedCount} obsolete chunks from SQLite for URL ${urlPrefix}`);
1118
1296
  }
1119
-
1120
- private removeObsoleteFilesSQLite(db: Database, processedFiles: Set<string>, dirPrefix: string, logger: Logger) {
1121
- const getChunksForDirStmt = db.prepare(`
1122
- SELECT chunk_id, url FROM vec_items
1123
- WHERE url LIKE 'file://%' AND url LIKE ?
1124
- `);
1125
- const deleteChunkStmt = db.prepare(`DELETE FROM vec_items WHERE chunk_id = ?`);
1126
-
1127
- const cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
1128
- const filePrefix = `file://${cleanedDirPrefix}`;
1129
-
1130
- const existingChunks = getChunksForDirStmt.all(`${filePrefix}%`) as { chunk_id: string; url: string }[];
1131
- let deletedCount = 0;
1132
-
1133
- const transaction = db.transaction(() => {
1134
- for (const { chunk_id, url } of existingChunks) {
1135
- // Remove file:// prefix to match with processedFiles
1136
- const filePath = url.substring(7);
1137
- if (!processedFiles.has(filePath)) {
1138
- logger.debug(`Deleting obsolete chunk from SQLite: ${chunk_id.substring(0, 8)}... (File not processed)`);
1139
- deleteChunkStmt.run(chunk_id);
1140
- deletedCount++;
1141
- }
1142
- }
1143
- });
1144
- transaction();
1145
-
1146
- logger.info(`Deleted ${deletedCount} obsolete chunks from SQLite for directory ${dirPrefix}`);
1147
- }
1148
-
1149
- private async removeObsoleteFilesQdrant(db: QdrantDB, processedFiles: Set<string>, dirPrefix: string, logger: Logger) {
1150
- const { client, collectionName } = db;
1151
- try {
1152
-
1153
- const cleanedDirPrefix = dirPrefix.replace(/^\.\/+/, '');
1154
- const filePrefix = `file://${cleanedDirPrefix}`;
1155
-
1156
- // Get all points that match the file prefix but are not metadata points
1157
- const response = await client.scroll(collectionName, {
1158
- limit: 10000,
1159
- with_payload: true,
1160
- with_vector: false,
1161
- filter: {
1162
- must: [
1163
- {
1164
- key: "url",
1165
- match: {
1166
- text: `${filePrefix}`
1167
- }
1168
- }
1169
- ],
1170
- must_not: [
1171
- {
1172
- key: "is_metadata",
1173
- match: {
1174
- value: true
1175
- }
1176
- }
1177
- ]
1178
- }
1179
- });
1180
-
1181
- const obsoletePointIds = response.points
1182
- .filter((point: any) => {
1183
- const url = point.payload?.url;
1184
- // Double check it's not a metadata record
1185
- if (point.payload?.is_metadata === true) {
1186
- return false;
1187
- }
1188
- // Remove file:// prefix to match with processedFiles
1189
- const filePath = url && url.startsWith('file://') ? url.substring(7) : '';
1190
- return filePath && !processedFiles.has(filePath);
1191
- })
1192
- .map((point: any) => point.id);
1193
-
1194
- if (obsoletePointIds.length > 0) {
1195
- await client.delete(collectionName, {
1196
- points: obsoletePointIds,
1197
- });
1198
- logger.info(`Deleted ${obsoletePointIds.length} obsolete chunks from Qdrant for directory ${dirPrefix}`);
1199
- } else {
1200
- logger.info(`No obsolete chunks to delete from Qdrant for directory ${dirPrefix}`);
1201
- }
1202
- } catch (error) {
1203
- logger.error(`Error removing obsolete chunks from Qdrant:`, error);
1204
- }
1205
- }
1206
1297
 
1207
1298
  private isValidUuid(str: string): boolean {
1208
1299
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;