@mhalder/qdrant-mcp-server 3.3.3 → 3.3.4

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 (168) hide show
  1. package/.github/workflows/ci.yml +0 -2
  2. package/.github/workflows/claude-code-review.yml +1 -1
  3. package/CHANGELOG.md +6 -0
  4. package/README.md +1 -1
  5. package/biome.json +3 -2
  6. package/build/code/chunker/tree-sitter-chunker.d.ts.map +1 -1
  7. package/build/code/chunker/tree-sitter-chunker.js +2 -12
  8. package/build/code/chunker/tree-sitter-chunker.js.map +1 -1
  9. package/build/code/indexer.d.ts.map +1 -1
  10. package/build/code/indexer.js +12 -18
  11. package/build/code/indexer.js.map +1 -1
  12. package/build/code/scanner.js +1 -1
  13. package/build/code/scanner.js.map +1 -1
  14. package/build/embeddings/cohere.d.ts +1 -1
  15. package/build/embeddings/cohere.d.ts.map +1 -1
  16. package/build/embeddings/cohere.js +2 -2
  17. package/build/embeddings/cohere.js.map +1 -1
  18. package/build/embeddings/cohere.test.js +1 -5
  19. package/build/embeddings/cohere.test.js.map +1 -1
  20. package/build/embeddings/factory.d.ts +1 -1
  21. package/build/embeddings/factory.d.ts.map +1 -1
  22. package/build/embeddings/factory.js +7 -9
  23. package/build/embeddings/factory.js.map +1 -1
  24. package/build/embeddings/factory.test.js +3 -3
  25. package/build/embeddings/factory.test.js.map +1 -1
  26. package/build/embeddings/ollama.d.ts +1 -1
  27. package/build/embeddings/ollama.d.ts.map +1 -1
  28. package/build/embeddings/ollama.js +6 -8
  29. package/build/embeddings/ollama.js.map +1 -1
  30. package/build/embeddings/ollama.test.js +2 -6
  31. package/build/embeddings/ollama.test.js.map +1 -1
  32. package/build/embeddings/openai.d.ts +1 -1
  33. package/build/embeddings/openai.d.ts.map +1 -1
  34. package/build/embeddings/openai.js +4 -7
  35. package/build/embeddings/openai.js.map +1 -1
  36. package/build/embeddings/openai.test.js +3 -12
  37. package/build/embeddings/openai.test.js.map +1 -1
  38. package/build/embeddings/sparse.test.js +12 -2
  39. package/build/embeddings/sparse.test.js.map +1 -1
  40. package/build/embeddings/voyage.d.ts +1 -1
  41. package/build/embeddings/voyage.d.ts.map +1 -1
  42. package/build/embeddings/voyage.js +2 -3
  43. package/build/embeddings/voyage.js.map +1 -1
  44. package/build/embeddings/voyage.test.js +2 -6
  45. package/build/embeddings/voyage.test.js.map +1 -1
  46. package/build/git/chunker.d.ts.map +1 -1
  47. package/build/git/chunker.js +2 -2
  48. package/build/git/chunker.js.map +1 -1
  49. package/build/git/chunker.test.js +1 -1
  50. package/build/git/chunker.test.js.map +1 -1
  51. package/build/git/extractor.d.ts.map +1 -1
  52. package/build/git/extractor.integration.test.js +9 -5
  53. package/build/git/extractor.integration.test.js.map +1 -1
  54. package/build/git/extractor.js +1 -1
  55. package/build/git/extractor.js.map +1 -1
  56. package/build/git/extractor.test.js +2 -2
  57. package/build/git/extractor.test.js.map +1 -1
  58. package/build/git/index.d.ts +4 -4
  59. package/build/git/index.d.ts.map +1 -1
  60. package/build/git/index.js +3 -3
  61. package/build/git/index.js.map +1 -1
  62. package/build/git/indexer.d.ts.map +1 -1
  63. package/build/git/indexer.js +9 -21
  64. package/build/git/indexer.js.map +1 -1
  65. package/build/git/indexer.test.js +4 -8
  66. package/build/git/indexer.test.js.map +1 -1
  67. package/build/git/sync/synchronizer.d.ts.map +1 -1
  68. package/build/git/sync/synchronizer.js.map +1 -1
  69. package/build/git/sync/synchronizer.test.js +4 -2
  70. package/build/git/sync/synchronizer.test.js.map +1 -1
  71. package/build/index.js +5 -9
  72. package/build/index.js.map +1 -1
  73. package/build/index.test.js +3 -3
  74. package/build/index.test.js.map +1 -1
  75. package/build/logger.d.ts.map +1 -1
  76. package/build/logger.js +1 -9
  77. package/build/logger.js.map +1 -1
  78. package/build/prompts/register.d.ts.map +1 -1
  79. package/build/prompts/register.js.map +1 -1
  80. package/build/qdrant/client.d.ts.map +1 -1
  81. package/build/qdrant/client.js.map +1 -1
  82. package/build/qdrant/client.test.js +10 -34
  83. package/build/qdrant/client.test.js.map +1 -1
  84. package/build/resources/index.d.ts +1 -1
  85. package/build/resources/index.d.ts.map +1 -1
  86. package/build/resources/index.js +1 -1
  87. package/build/resources/index.js.map +1 -1
  88. package/build/tools/code.d.ts.map +1 -1
  89. package/build/tools/code.js +3 -9
  90. package/build/tools/code.js.map +1 -1
  91. package/build/tools/collection.d.ts.map +1 -1
  92. package/build/tools/collection.js +1 -3
  93. package/build/tools/collection.js.map +1 -1
  94. package/build/tools/document.d.ts.map +1 -1
  95. package/build/tools/document.js +1 -1
  96. package/build/tools/document.js.map +1 -1
  97. package/build/tools/federated.d.ts.map +1 -1
  98. package/build/tools/federated.js +15 -6
  99. package/build/tools/federated.js.map +1 -1
  100. package/build/tools/federated.test.js +18 -22
  101. package/build/tools/federated.test.js.map +1 -1
  102. package/build/tools/git-history.d.ts.map +1 -1
  103. package/build/tools/git-history.js +3 -7
  104. package/build/tools/git-history.js.map +1 -1
  105. package/build/tools/index.d.ts.map +1 -1
  106. package/build/tools/index.js.map +1 -1
  107. package/build/tools/logging.d.ts.map +1 -1
  108. package/build/tools/logging.js +1 -3
  109. package/build/tools/logging.js.map +1 -1
  110. package/build/tools/logging.test.js +1 -1
  111. package/build/tools/logging.test.js.map +1 -1
  112. package/build/tools/schemas.d.ts.map +1 -1
  113. package/build/tools/schemas.js +17 -64
  114. package/build/tools/schemas.js.map +1 -1
  115. package/build/tools/search.d.ts.map +1 -1
  116. package/build/tools/search.js +1 -1
  117. package/build/tools/search.js.map +1 -1
  118. package/commitlint.config.js +12 -23
  119. package/package.json +1 -1
  120. package/scripts/verify-providers.js +12 -32
  121. package/src/code/chunker/tree-sitter-chunker.ts +9 -35
  122. package/src/code/indexer.ts +45 -107
  123. package/src/code/scanner.ts +1 -1
  124. package/src/embeddings/cohere.test.ts +17 -45
  125. package/src/embeddings/cohere.ts +10 -17
  126. package/src/embeddings/factory.test.ts +18 -18
  127. package/src/embeddings/factory.ts +18 -25
  128. package/src/embeddings/ollama.test.ts +38 -67
  129. package/src/embeddings/ollama.ts +15 -27
  130. package/src/embeddings/openai.test.ts +17 -53
  131. package/src/embeddings/openai.ts +11 -22
  132. package/src/embeddings/sparse.test.ts +12 -2
  133. package/src/embeddings/voyage.test.ts +39 -80
  134. package/src/embeddings/voyage.ts +9 -13
  135. package/src/git/chunker.test.ts +1 -1
  136. package/src/git/chunker.ts +6 -22
  137. package/src/git/extractor.integration.test.ts +12 -16
  138. package/src/git/extractor.test.ts +21 -35
  139. package/src/git/extractor.ts +14 -36
  140. package/src/git/index.ts +9 -10
  141. package/src/git/indexer.test.ts +29 -57
  142. package/src/git/indexer.ts +38 -86
  143. package/src/git/sync/synchronizer.test.ts +6 -9
  144. package/src/git/sync/synchronizer.ts +2 -5
  145. package/src/index.test.ts +7 -9
  146. package/src/index.ts +34 -80
  147. package/src/logger.ts +3 -14
  148. package/src/prompts/register.ts +3 -10
  149. package/src/qdrant/client.test.ts +63 -169
  150. package/src/qdrant/client.ts +19 -45
  151. package/src/resources/index.ts +4 -10
  152. package/src/tools/code.ts +43 -66
  153. package/src/tools/collection.ts +19 -38
  154. package/src/tools/document.ts +10 -19
  155. package/src/tools/federated.test.ts +34 -57
  156. package/src/tools/federated.ts +88 -108
  157. package/src/tools/git-history.ts +32 -60
  158. package/src/tools/index.ts +1 -4
  159. package/src/tools/logging.test.ts +10 -10
  160. package/src/tools/logging.ts +8 -18
  161. package/src/tools/schemas.ts +23 -78
  162. package/src/tools/search.ts +77 -94
  163. package/tests/code/chunker/tree-sitter-chunker.test.ts +6 -19
  164. package/tests/code/indexer.test.ts +100 -192
  165. package/tests/code/integration.test.ts +61 -117
  166. package/tests/code/scanner.test.ts +12 -39
  167. package/tests/code/sync/snapshot.test.ts +4 -14
  168. package/tests/code/sync/synchronizer.test.ts +10 -40
@@ -1,9 +1,9 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
- import { EmbeddingProviderFactory, type FactoryConfig } from "./factory.js";
3
- import { OpenAIEmbeddings } from "./openai.js";
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
2
  import { CohereEmbeddings } from "./cohere.js";
5
- import { VoyageEmbeddings } from "./voyage.js";
3
+ import { EmbeddingProviderFactory } from "./factory.js";
6
4
  import { OllamaEmbeddings } from "./ollama.js";
5
+ import { OpenAIEmbeddings } from "./openai.js";
6
+ import { VoyageEmbeddings } from "./voyage.js";
7
7
 
8
8
  vi.mock("../logger.js", () => ({
9
9
  default: {
@@ -36,7 +36,7 @@ describe("EmbeddingProviderFactory", () => {
36
36
  expect(() =>
37
37
  EmbeddingProviderFactory.create({
38
38
  provider: "unknown" as any,
39
- }),
39
+ })
40
40
  ).toThrow("Unknown embedding provider: unknown");
41
41
  });
42
42
 
@@ -44,7 +44,7 @@ describe("EmbeddingProviderFactory", () => {
44
44
  expect(() =>
45
45
  EmbeddingProviderFactory.create({
46
46
  provider: "invalid" as any,
47
- }),
47
+ })
48
48
  ).toThrow("openai, cohere, voyage, ollama");
49
49
  });
50
50
  });
@@ -54,7 +54,7 @@ describe("EmbeddingProviderFactory", () => {
54
54
  expect(() =>
55
55
  EmbeddingProviderFactory.create({
56
56
  provider: "openai",
57
- }),
57
+ })
58
58
  ).toThrow("API key is required for OpenAI provider");
59
59
  });
60
60
 
@@ -110,7 +110,7 @@ describe("EmbeddingProviderFactory", () => {
110
110
  expect(() =>
111
111
  EmbeddingProviderFactory.create({
112
112
  provider: "cohere",
113
- }),
113
+ })
114
114
  ).toThrow("API key is required for Cohere provider");
115
115
  });
116
116
 
@@ -151,7 +151,7 @@ describe("EmbeddingProviderFactory", () => {
151
151
  expect(() =>
152
152
  EmbeddingProviderFactory.create({
153
153
  provider: "voyage",
154
- }),
154
+ })
155
155
  ).toThrow("API key is required for Voyage AI provider");
156
156
  });
157
157
 
@@ -357,7 +357,7 @@ describe("EmbeddingProviderFactory", () => {
357
357
  process.env.EMBEDDING_DIMENSIONS = "not-a-number";
358
358
 
359
359
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
360
- 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "not-a-number"',
360
+ 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "not-a-number"'
361
361
  );
362
362
  });
363
363
 
@@ -367,7 +367,7 @@ describe("EmbeddingProviderFactory", () => {
367
367
  process.env.EMBEDDING_DIMENSIONS = "-100";
368
368
 
369
369
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
370
- 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "-100"',
370
+ 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "-100"'
371
371
  );
372
372
  });
373
373
 
@@ -377,7 +377,7 @@ describe("EmbeddingProviderFactory", () => {
377
377
  process.env.EMBEDDING_DIMENSIONS = "0";
378
378
 
379
379
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
380
- 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "0"',
380
+ 'Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "0"'
381
381
  );
382
382
  });
383
383
 
@@ -387,7 +387,7 @@ describe("EmbeddingProviderFactory", () => {
387
387
  process.env.EMBEDDING_MAX_REQUESTS_PER_MINUTE = "invalid";
388
388
 
389
389
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
390
- 'Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "invalid"',
390
+ 'Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "invalid"'
391
391
  );
392
392
  });
393
393
 
@@ -397,7 +397,7 @@ describe("EmbeddingProviderFactory", () => {
397
397
  process.env.EMBEDDING_MAX_REQUESTS_PER_MINUTE = "-50";
398
398
 
399
399
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
400
- 'Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "-50"',
400
+ 'Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "-50"'
401
401
  );
402
402
  });
403
403
 
@@ -407,7 +407,7 @@ describe("EmbeddingProviderFactory", () => {
407
407
  process.env.EMBEDDING_RETRY_ATTEMPTS = "abc";
408
408
 
409
409
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
410
- 'Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "abc"',
410
+ 'Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "abc"'
411
411
  );
412
412
  });
413
413
 
@@ -417,7 +417,7 @@ describe("EmbeddingProviderFactory", () => {
417
417
  process.env.EMBEDDING_RETRY_ATTEMPTS = "-5";
418
418
 
419
419
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
420
- 'Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "-5"',
420
+ 'Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "-5"'
421
421
  );
422
422
  });
423
423
 
@@ -427,7 +427,7 @@ describe("EmbeddingProviderFactory", () => {
427
427
  process.env.EMBEDDING_RETRY_DELAY = "xyz";
428
428
 
429
429
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
430
- 'Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "xyz"',
430
+ 'Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "xyz"'
431
431
  );
432
432
  });
433
433
 
@@ -437,7 +437,7 @@ describe("EmbeddingProviderFactory", () => {
437
437
  process.env.EMBEDDING_RETRY_DELAY = "-1000";
438
438
 
439
439
  expect(() => EmbeddingProviderFactory.createFromEnv()).toThrow(
440
- 'Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "-1000"',
440
+ 'Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "-1000"'
441
441
  );
442
442
  });
443
443
 
@@ -1,9 +1,9 @@
1
1
  import logger from "../logger.js";
2
- import { EmbeddingProvider, ProviderConfig } from "./base.js";
3
- import { OpenAIEmbeddings } from "./openai.js";
2
+ import type { EmbeddingProvider, ProviderConfig } from "./base.js";
4
3
  import { CohereEmbeddings } from "./cohere.js";
5
- import { VoyageEmbeddings } from "./voyage.js";
6
4
  import { OllamaEmbeddings } from "./ollama.js";
5
+ import { OpenAIEmbeddings } from "./openai.js";
6
+ import { VoyageEmbeddings } from "./voyage.js";
7
7
 
8
8
  export type EmbeddingProviderType = "openai" | "cohere" | "voyage" | "ollama";
9
9
 
@@ -13,8 +13,7 @@ export interface FactoryConfig extends ProviderConfig {
13
13
 
14
14
  export class EmbeddingProviderFactory {
15
15
  static create(config: FactoryConfig): EmbeddingProvider {
16
- const { provider, model, dimensions, rateLimitConfig, apiKey, baseUrl } =
17
- config;
16
+ const { provider, model, dimensions, rateLimitConfig, apiKey, baseUrl } = config;
18
17
 
19
18
  logger.info({ provider, model }, "Creating embedding provider");
20
19
 
@@ -27,7 +26,7 @@ export class EmbeddingProviderFactory {
27
26
  apiKey,
28
27
  model || "text-embedding-3-small",
29
28
  dimensions,
30
- rateLimitConfig,
29
+ rateLimitConfig
31
30
  );
32
31
 
33
32
  case "cohere":
@@ -38,7 +37,7 @@ export class EmbeddingProviderFactory {
38
37
  apiKey,
39
38
  model || "embed-english-v3.0",
40
39
  dimensions,
41
- rateLimitConfig,
40
+ rateLimitConfig
42
41
  );
43
42
 
44
43
  case "voyage":
@@ -50,7 +49,7 @@ export class EmbeddingProviderFactory {
50
49
  model || "voyage-2",
51
50
  dimensions,
52
51
  rateLimitConfig,
53
- baseUrl || "https://api.voyageai.com/v1",
52
+ baseUrl || "https://api.voyageai.com/v1"
54
53
  );
55
54
 
56
55
  case "ollama":
@@ -58,12 +57,12 @@ export class EmbeddingProviderFactory {
58
57
  model || "nomic-embed-text",
59
58
  dimensions,
60
59
  rateLimitConfig,
61
- baseUrl || "http://localhost:11434",
60
+ baseUrl || "http://localhost:11434"
62
61
  );
63
62
 
64
63
  default:
65
64
  throw new Error(
66
- `Unknown embedding provider: ${provider}. Supported providers: openai, cohere, voyage, ollama`,
65
+ `Unknown embedding provider: ${provider}. Supported providers: openai, cohere, voyage, ollama`
67
66
  );
68
67
  }
69
68
  }
@@ -97,9 +96,9 @@ export class EmbeddingProviderFactory {
97
96
  : undefined;
98
97
 
99
98
  // Validate dimensions
100
- if (dimensions !== undefined && (isNaN(dimensions) || dimensions <= 0)) {
99
+ if (dimensions !== undefined && (Number.isNaN(dimensions) || dimensions <= 0)) {
101
100
  throw new Error(
102
- `Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "${process.env.EMBEDDING_DIMENSIONS}"`,
101
+ `Invalid EMBEDDING_DIMENSIONS: must be a positive integer, got "${process.env.EMBEDDING_DIMENSIONS}"`
103
102
  );
104
103
  }
105
104
 
@@ -113,10 +112,10 @@ export class EmbeddingProviderFactory {
113
112
  // Validate maxRequestsPerMinute
114
113
  if (
115
114
  maxRequestsPerMinute !== undefined &&
116
- (isNaN(maxRequestsPerMinute) || maxRequestsPerMinute <= 0)
115
+ (Number.isNaN(maxRequestsPerMinute) || maxRequestsPerMinute <= 0)
117
116
  ) {
118
117
  throw new Error(
119
- `Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "${process.env.EMBEDDING_MAX_REQUESTS_PER_MINUTE}"`,
118
+ `Invalid EMBEDDING_MAX_REQUESTS_PER_MINUTE: must be a positive integer, got "${process.env.EMBEDDING_MAX_REQUESTS_PER_MINUTE}"`
120
119
  );
121
120
  }
122
121
 
@@ -125,12 +124,9 @@ export class EmbeddingProviderFactory {
125
124
  : undefined;
126
125
 
127
126
  // Validate retryAttempts
128
- if (
129
- retryAttempts !== undefined &&
130
- (isNaN(retryAttempts) || retryAttempts < 0)
131
- ) {
127
+ if (retryAttempts !== undefined && (Number.isNaN(retryAttempts) || retryAttempts < 0)) {
132
128
  throw new Error(
133
- `Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "${process.env.EMBEDDING_RETRY_ATTEMPTS}"`,
129
+ `Invalid EMBEDDING_RETRY_ATTEMPTS: must be a non-negative integer, got "${process.env.EMBEDDING_RETRY_ATTEMPTS}"`
134
130
  );
135
131
  }
136
132
 
@@ -139,12 +135,9 @@ export class EmbeddingProviderFactory {
139
135
  : undefined;
140
136
 
141
137
  // Validate retryDelayMs
142
- if (
143
- retryDelayMs !== undefined &&
144
- (isNaN(retryDelayMs) || retryDelayMs < 0)
145
- ) {
138
+ if (retryDelayMs !== undefined && (Number.isNaN(retryDelayMs) || retryDelayMs < 0)) {
146
139
  throw new Error(
147
- `Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "${process.env.EMBEDDING_RETRY_DELAY}"`,
140
+ `Invalid EMBEDDING_RETRY_DELAY: must be a non-negative integer, got "${process.env.EMBEDDING_RETRY_DELAY}"`
148
141
  );
149
142
  }
150
143
 
@@ -154,7 +147,7 @@ export class EmbeddingProviderFactory {
154
147
  retryDelayMs,
155
148
  };
156
149
 
157
- return this.create({
150
+ return EmbeddingProviderFactory.create({
158
151
  provider,
159
152
  model,
160
153
  dimensions,
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  import { OllamaEmbeddings } from "./ollama.js";
3
3
 
4
4
  // Mock fetch globally
@@ -54,7 +54,7 @@ describe("OllamaEmbeddings", () => {
54
54
  "nomic-embed-text",
55
55
  undefined,
56
56
  undefined,
57
- "http://custom:11434",
57
+ "http://custom:11434"
58
58
  );
59
59
  expect(customEmbeddings).toBeDefined();
60
60
  });
@@ -89,19 +89,16 @@ describe("OllamaEmbeddings", () => {
89
89
  embedding: mockEmbedding,
90
90
  dimensions: 768,
91
91
  });
92
- expect(mockFetch).toHaveBeenCalledWith(
93
- "http://localhost:11434/api/embeddings",
94
- {
95
- method: "POST",
96
- headers: {
97
- "Content-Type": "application/json",
98
- },
99
- body: JSON.stringify({
100
- model: "nomic-embed-text",
101
- prompt: "test text",
102
- }),
92
+ expect(mockFetch).toHaveBeenCalledWith("http://localhost:11434/api/embeddings", {
93
+ method: "POST",
94
+ headers: {
95
+ "Content-Type": "application/json",
103
96
  },
104
- );
97
+ body: JSON.stringify({
98
+ model: "nomic-embed-text",
99
+ prompt: "test text",
100
+ }),
101
+ });
105
102
  });
106
103
 
107
104
  it("should handle long text", async () => {
@@ -124,7 +121,7 @@ describe("OllamaEmbeddings", () => {
124
121
  "nomic-embed-text",
125
122
  undefined,
126
123
  undefined,
127
- "http://custom:11434",
124
+ "http://custom:11434"
128
125
  );
129
126
 
130
127
  const mockEmbedding = Array(768).fill(0.1);
@@ -139,7 +136,7 @@ describe("OllamaEmbeddings", () => {
139
136
 
140
137
  expect(mockFetch).toHaveBeenCalledWith(
141
138
  "http://custom:11434/api/embeddings",
142
- expect.any(Object),
139
+ expect.any(Object)
143
140
  );
144
141
  });
145
142
 
@@ -150,7 +147,7 @@ describe("OllamaEmbeddings", () => {
150
147
  });
151
148
 
152
149
  await expect(embeddings.embed("test")).rejects.toThrow(
153
- "No embedding returned from Ollama API",
150
+ "No embedding returned from Ollama API"
154
151
  );
155
152
  });
156
153
 
@@ -181,7 +178,7 @@ describe("OllamaEmbeddings", () => {
181
178
  await expect(embeddings.embed(longText)).rejects.toThrow(
182
179
  expect.objectContaining({
183
180
  message: expect.stringContaining("Text preview:"),
184
- }),
181
+ })
185
182
  );
186
183
  });
187
184
 
@@ -189,7 +186,7 @@ describe("OllamaEmbeddings", () => {
189
186
  mockFetch.mockRejectedValue("Connection refused");
190
187
 
191
188
  await expect(embeddings.embed("test")).rejects.toThrow(
192
- "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text",
189
+ "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text"
193
190
  );
194
191
  });
195
192
 
@@ -198,27 +195,21 @@ describe("OllamaEmbeddings", () => {
198
195
  message: "Custom error message",
199
196
  });
200
197
 
201
- await expect(embeddings.embed("test")).rejects.toThrow(
202
- "Custom error message",
203
- );
198
+ await expect(embeddings.embed("test")).rejects.toThrow("Custom error message");
204
199
  });
205
200
 
206
201
  it("should handle non-Error objects in catch block", async () => {
207
202
  mockFetch.mockRejectedValue({ code: "ERR_UNKNOWN", details: "info" });
208
203
 
209
204
  await expect(embeddings.embed("test")).rejects.toThrow(
210
- "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text",
205
+ "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text"
211
206
  );
212
207
  });
213
208
  });
214
209
 
215
210
  describe("embedBatch", () => {
216
211
  it("should generate embeddings for multiple texts in parallel", async () => {
217
- const mockEmbeddings = [
218
- Array(768).fill(0.1),
219
- Array(768).fill(0.2),
220
- Array(768).fill(0.3),
221
- ];
212
+ const mockEmbeddings = [Array(768).fill(0.1), Array(768).fill(0.2), Array(768).fill(0.3)];
222
213
 
223
214
  // Mock sequential calls for each text
224
215
  mockEmbeddings.forEach((embedding) => {
@@ -290,9 +281,7 @@ describe("OllamaEmbeddings", () => {
290
281
  })
291
282
  .mockRejectedValueOnce(new Error("Batch API Error"));
292
283
 
293
- await expect(embeddings.embedBatch(["text1", "text2"])).rejects.toThrow(
294
- "Batch API Error",
295
- );
284
+ await expect(embeddings.embedBatch(["text1", "text2"])).rejects.toThrow("Batch API Error");
296
285
  });
297
286
 
298
287
  it("should handle partial failures in batch", async () => {
@@ -378,14 +367,10 @@ describe("OllamaEmbeddings", () => {
378
367
  });
379
368
 
380
369
  it("should use exponential backoff with faster default delay", async () => {
381
- const rateLimitEmbeddings = new OllamaEmbeddings(
382
- "nomic-embed-text",
383
- undefined,
384
- {
385
- retryAttempts: 3,
386
- retryDelayMs: 100,
387
- },
388
- );
370
+ const rateLimitEmbeddings = new OllamaEmbeddings("nomic-embed-text", undefined, {
371
+ retryAttempts: 3,
372
+ retryDelayMs: 100,
373
+ });
389
374
 
390
375
  const mockEmbedding = Array(768).fill(0.5);
391
376
 
@@ -414,14 +399,10 @@ describe("OllamaEmbeddings", () => {
414
399
  });
415
400
 
416
401
  it("should throw error after max retries exceeded", async () => {
417
- const rateLimitEmbeddings = new OllamaEmbeddings(
418
- "nomic-embed-text",
419
- undefined,
420
- {
421
- retryAttempts: 2,
422
- retryDelayMs: 100,
423
- },
424
- );
402
+ const rateLimitEmbeddings = new OllamaEmbeddings("nomic-embed-text", undefined, {
403
+ retryAttempts: 2,
404
+ retryDelayMs: 100,
405
+ });
425
406
 
426
407
  mockFetch.mockResolvedValue({
427
408
  ok: false,
@@ -430,7 +411,7 @@ describe("OllamaEmbeddings", () => {
430
411
  });
431
412
 
432
413
  await expect(rateLimitEmbeddings.embed("test text")).rejects.toThrow(
433
- "Ollama API rate limit exceeded after 2 retry attempts",
414
+ "Ollama API rate limit exceeded after 2 retry attempts"
434
415
  );
435
416
 
436
417
  expect(mockFetch).toHaveBeenCalledTimes(3);
@@ -473,15 +454,11 @@ describe("OllamaEmbeddings", () => {
473
454
  });
474
455
 
475
456
  it("should accept custom rate limit configuration", () => {
476
- const customEmbeddings = new OllamaEmbeddings(
477
- "nomic-embed-text",
478
- undefined,
479
- {
480
- maxRequestsPerMinute: 2000,
481
- retryAttempts: 5,
482
- retryDelayMs: 1000,
483
- },
484
- );
457
+ const customEmbeddings = new OllamaEmbeddings("nomic-embed-text", undefined, {
458
+ maxRequestsPerMinute: 2000,
459
+ retryAttempts: 5,
460
+ retryDelayMs: 1000,
461
+ });
485
462
 
486
463
  expect(customEmbeddings).toBeDefined();
487
464
  });
@@ -503,9 +480,7 @@ describe("OllamaEmbeddings", () => {
503
480
  it("should handle string primitive errors", async () => {
504
481
  mockFetch.mockRejectedValue("Network unreachable");
505
482
 
506
- await expect(embeddings.embed("test")).rejects.toThrow(
507
- "Network unreachable",
508
- );
483
+ await expect(embeddings.embed("test")).rejects.toThrow("Network unreachable");
509
484
  });
510
485
 
511
486
  it("should handle error objects with non-string message property", async () => {
@@ -523,9 +498,7 @@ describe("OllamaEmbeddings", () => {
523
498
  const testError = new Error("Connection timeout");
524
499
  mockFetch.mockRejectedValue(testError);
525
500
 
526
- await expect(embeddings.embed("test")).rejects.toThrow(
527
- "Connection timeout",
528
- );
501
+ await expect(embeddings.embed("test")).rejects.toThrow("Connection timeout");
529
502
  });
530
503
 
531
504
  it("should handle Error instance from network error with enhanced message", async () => {
@@ -534,7 +507,7 @@ describe("OllamaEmbeddings", () => {
534
507
  mockFetch.mockRejectedValue(networkError);
535
508
 
536
509
  await expect(embeddings.embed("test")).rejects.toThrow(
537
- "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text: ECONNREFUSED. Text preview:",
510
+ "Failed to call Ollama API at http://localhost:11434 with model nomic-embed-text: ECONNREFUSED. Text preview:"
538
511
  );
539
512
  });
540
513
 
@@ -547,9 +520,7 @@ describe("OllamaEmbeddings", () => {
547
520
  };
548
521
  mockFetch.mockRejectedValue(customError);
549
522
 
550
- await expect(embeddings.embed("test")).rejects.toThrow(
551
- "Custom API failure",
552
- );
523
+ await expect(embeddings.embed("test")).rejects.toThrow("Custom API failure");
553
524
  });
554
525
  });
555
526
  });
@@ -1,6 +1,6 @@
1
1
  import Bottleneck from "bottleneck";
2
2
  import logger from "../logger.js";
3
- import { EmbeddingProvider, EmbeddingResult, RateLimitConfig } from "./base.js";
3
+ import type { EmbeddingProvider, EmbeddingResult, RateLimitConfig } from "./base.js";
4
4
 
5
5
  interface OllamaError {
6
6
  status?: number;
@@ -24,7 +24,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
24
24
  model: string = "nomic-embed-text",
25
25
  dimensions?: number,
26
26
  rateLimitConfig?: RateLimitConfig,
27
- baseUrl: string = "http://localhost:11434",
27
+ baseUrl: string = "http://localhost:11434"
28
28
  ) {
29
29
  this.model = model;
30
30
  this.baseUrl = baseUrl;
@@ -53,22 +53,15 @@ export class OllamaEmbeddings implements EmbeddingProvider {
53
53
  }
54
54
 
55
55
  private isOllamaError(e: unknown): e is OllamaError {
56
- return (
57
- typeof e === "object" && e !== null && ("status" in e || "message" in e)
58
- );
56
+ return typeof e === "object" && e !== null && ("status" in e || "message" in e);
59
57
  }
60
58
 
61
- private async retryWithBackoff<T>(
62
- fn: () => Promise<T>,
63
- attempt: number = 0,
64
- ): Promise<T> {
59
+ private async retryWithBackoff<T>(fn: () => Promise<T>, attempt: number = 0): Promise<T> {
65
60
  try {
66
61
  return await fn();
67
62
  } catch (error: unknown) {
68
63
  // Type guard for OllamaError
69
- const apiError = this.isOllamaError(error)
70
- ? error
71
- : { status: 0, message: String(error) };
64
+ const apiError = this.isOllamaError(error) ? error : { status: 0, message: String(error) };
72
65
 
73
66
  const isRateLimitError =
74
67
  apiError.status === 429 ||
@@ -76,7 +69,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
76
69
  apiError.message.toLowerCase().includes("rate limit"));
77
70
 
78
71
  if (isRateLimitError && attempt < this.retryAttempts) {
79
- const delayMs = this.retryDelayMs * Math.pow(2, attempt);
72
+ const delayMs = this.retryDelayMs * 2 ** attempt;
80
73
  const waitTimeSeconds = (delayMs / 1000).toFixed(1);
81
74
  this.log.warn(
82
75
  {
@@ -84,7 +77,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
84
77
  attempt: attempt + 1,
85
78
  maxAttempts: this.retryAttempts,
86
79
  },
87
- "Rate limit reached, retrying",
80
+ "Rate limit reached, retrying"
88
81
  );
89
82
 
90
83
  await new Promise((resolve) => setTimeout(resolve, delayMs));
@@ -93,7 +86,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
93
86
 
94
87
  if (isRateLimitError) {
95
88
  throw new Error(
96
- `Ollama API rate limit exceeded after ${this.retryAttempts} retry attempts. Please try again later or reduce request frequency.`,
89
+ `Ollama API rate limit exceeded after ${this.retryAttempts} retry attempts. Please try again later or reduce request frequency.`
97
90
  );
98
91
  }
99
92
 
@@ -116,8 +109,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
116
109
 
117
110
  if (!response.ok) {
118
111
  const errorBody = await response.text();
119
- const textPreview =
120
- text.length > 100 ? text.substring(0, 100) + "..." : text;
112
+ const textPreview = text.length > 100 ? `${text.substring(0, 100)}...` : text;
121
113
  const error: OllamaError = {
122
114
  status: response.status,
123
115
  message: `Ollama API error (${response.status}) for model "${this.model}": ${errorBody}. Text preview: "${textPreview}"`,
@@ -134,10 +126,9 @@ export class OllamaEmbeddings implements EmbeddingProvider {
134
126
 
135
127
  // For Error instances (like network errors), enhance the message
136
128
  if (error instanceof Error) {
137
- const textPreview =
138
- text.length > 100 ? text.substring(0, 100) + "..." : text;
129
+ const textPreview = text.length > 100 ? `${text.substring(0, 100)}...` : text;
139
130
  throw new Error(
140
- `Failed to call Ollama API at ${this.baseUrl} with model ${this.model}: ${error.message}. Text preview: "${textPreview}"`,
131
+ `Failed to call Ollama API at ${this.baseUrl} with model ${this.model}: ${error.message}. Text preview: "${textPreview}"`
141
132
  );
142
133
  }
143
134
 
@@ -148,12 +139,11 @@ export class OllamaEmbeddings implements EmbeddingProvider {
148
139
  }
149
140
 
150
141
  // For other types, create a descriptive error message
151
- const textPreview =
152
- text.length > 100 ? text.substring(0, 100) + "..." : text;
142
+ const textPreview = text.length > 100 ? `${text.substring(0, 100)}...` : text;
153
143
  const errorMessage = JSON.stringify(error);
154
144
 
155
145
  throw new Error(
156
- `Failed to call Ollama API at ${this.baseUrl} with model ${this.model}: ${errorMessage}. Text preview: "${textPreview}"`,
146
+ `Failed to call Ollama API at ${this.baseUrl} with model ${this.model}: ${errorMessage}. Text preview: "${textPreview}"`
157
147
  );
158
148
  }
159
149
  }
@@ -171,7 +161,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
171
161
  embedding: response.embedding,
172
162
  dimensions: this.dimensions,
173
163
  };
174
- }),
164
+ })
175
165
  );
176
166
  }
177
167
 
@@ -185,9 +175,7 @@ export class OllamaEmbeddings implements EmbeddingProvider {
185
175
  for (let i = 0; i < texts.length; i += CHUNK_SIZE) {
186
176
  const chunk = texts.slice(i, i + CHUNK_SIZE);
187
177
  // The Bottleneck limiter will handle rate limiting and concurrency (maxConcurrent: 10)
188
- const chunkResults = await Promise.all(
189
- chunk.map((text) => this.embed(text)),
190
- );
178
+ const chunkResults = await Promise.all(chunk.map((text) => this.embed(text)));
191
179
  results.push(...chunkResults);
192
180
  }
193
181