@graphext/cuery 0.4.0 → 0.5.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 (203) hide show
  1. package/esm/browser.d.ts +1 -1
  2. package/esm/browser.d.ts.map +1 -1
  3. package/esm/browser.js +1 -1
  4. package/esm/mod.d.ts +5 -2
  5. package/esm/mod.d.ts.map +1 -1
  6. package/esm/mod.js +7 -2
  7. package/esm/src/api.d.ts +36 -5
  8. package/esm/src/api.d.ts.map +1 -1
  9. package/esm/src/api.js +84 -37
  10. package/esm/src/apis/chatgptScraper/brightdata.js +1 -1
  11. package/esm/src/apis/chatgptScraper/oxy.js +1 -1
  12. package/esm/src/apis/chatgptScraper/scraper.js +2 -2
  13. package/esm/src/apis/hasdata/aim.js +1 -1
  14. package/esm/src/apis/hasdata/aio.js +1 -1
  15. package/esm/src/apis/hasdata/helpers.d.ts +1 -1
  16. package/esm/src/apis/hasdata/helpers.d.ts.map +1 -1
  17. package/esm/src/apis/hasdata/helpers.js +2 -2
  18. package/esm/src/assets/models.d.ts +60725 -0
  19. package/esm/src/assets/models.d.ts.map +1 -0
  20. package/esm/src/assets/models.js +71915 -0
  21. package/esm/src/helpers/async.d.ts.map +1 -0
  22. package/esm/src/{async.js → helpers/async.js} +1 -1
  23. package/esm/src/helpers/seedKeywords.d.ts.map +1 -0
  24. package/esm/src/helpers/urls.d.ts.map +1 -0
  25. package/esm/src/helpers/utils.d.ts.map +1 -0
  26. package/esm/src/llm.d.ts +35 -0
  27. package/esm/src/llm.d.ts.map +1 -0
  28. package/esm/src/llm.js +59 -0
  29. package/esm/src/providers/google.d.ts +12 -0
  30. package/esm/src/providers/google.d.ts.map +1 -0
  31. package/esm/src/providers/google.js +111 -0
  32. package/esm/src/providers/index.d.ts +13 -0
  33. package/esm/src/providers/index.d.ts.map +1 -0
  34. package/esm/src/providers/index.js +14 -0
  35. package/esm/src/providers/openai.d.ts +12 -0
  36. package/esm/src/providers/openai.d.ts.map +1 -0
  37. package/esm/src/providers/openai.js +141 -0
  38. package/esm/src/providers/pricing.d.ts +72 -0
  39. package/esm/src/providers/pricing.d.ts.map +1 -0
  40. package/esm/src/providers/pricing.js +88 -0
  41. package/esm/src/providers/registry.d.ts +20 -0
  42. package/esm/src/providers/registry.d.ts.map +1 -0
  43. package/esm/src/providers/registry.js +35 -0
  44. package/esm/src/providers/types.d.ts +49 -0
  45. package/esm/src/providers/types.d.ts.map +1 -0
  46. package/esm/src/providers/types.js +7 -0
  47. package/esm/src/response.d.ts +74 -0
  48. package/esm/src/response.d.ts.map +1 -0
  49. package/esm/src/response.js +110 -0
  50. package/esm/src/tool.d.ts +58 -0
  51. package/esm/src/tool.d.ts.map +1 -0
  52. package/esm/src/tool.js +91 -0
  53. package/esm/src/tools/brands.js +6 -6
  54. package/esm/src/tools/classifier.d.ts +80 -17
  55. package/esm/src/tools/classifier.d.ts.map +1 -1
  56. package/esm/src/tools/classifier.js +68 -80
  57. package/esm/src/tools/entities.d.ts +23 -12
  58. package/esm/src/tools/entities.d.ts.map +1 -1
  59. package/esm/src/tools/entities.js +27 -47
  60. package/esm/src/tools/funnel.js +7 -7
  61. package/esm/src/tools/generic.d.ts +17 -4
  62. package/esm/src/tools/generic.d.ts.map +1 -1
  63. package/esm/src/tools/generic.js +39 -14
  64. package/esm/src/tools/keywords.js +5 -5
  65. package/esm/src/tools/personas.d.ts +49 -2
  66. package/esm/src/tools/personas.d.ts.map +1 -1
  67. package/esm/src/tools/personas.js +59 -35
  68. package/esm/src/tools/scorer.d.ts +24 -6
  69. package/esm/src/tools/scorer.d.ts.map +1 -1
  70. package/esm/src/tools/scorer.js +27 -22
  71. package/esm/src/tools/search.d.ts.map +1 -1
  72. package/esm/src/tools/search.js +33 -9
  73. package/esm/src/tools/sentiment.d.ts +30 -8
  74. package/esm/src/tools/sentiment.d.ts.map +1 -1
  75. package/esm/src/tools/sentiment.js +33 -28
  76. package/esm/src/tools/sources.d.ts +5 -5
  77. package/esm/src/tools/sources.d.ts.map +1 -1
  78. package/esm/src/tools/sources.js +5 -6
  79. package/esm/src/tools/topics.d.ts +44 -16
  80. package/esm/src/tools/topics.d.ts.map +1 -1
  81. package/esm/src/tools/topics.js +77 -68
  82. package/esm/src/tools/translate.d.ts +22 -31
  83. package/esm/src/tools/translate.d.ts.map +1 -1
  84. package/esm/src/tools/translate.js +40 -36
  85. package/package.json +2 -1
  86. package/script/browser.d.ts +1 -1
  87. package/script/browser.d.ts.map +1 -1
  88. package/script/browser.js +1 -1
  89. package/script/mod.d.ts +5 -2
  90. package/script/mod.d.ts.map +1 -1
  91. package/script/mod.js +14 -2
  92. package/script/src/api.d.ts +36 -5
  93. package/script/src/api.d.ts.map +1 -1
  94. package/script/src/api.js +84 -35
  95. package/script/src/apis/chatgptScraper/brightdata.js +1 -1
  96. package/script/src/apis/chatgptScraper/oxy.js +1 -1
  97. package/script/src/apis/chatgptScraper/scraper.js +2 -2
  98. package/script/src/apis/hasdata/aim.js +1 -1
  99. package/script/src/apis/hasdata/aio.js +1 -1
  100. package/script/src/apis/hasdata/helpers.d.ts +1 -1
  101. package/script/src/apis/hasdata/helpers.d.ts.map +1 -1
  102. package/script/src/apis/hasdata/helpers.js +2 -2
  103. package/script/src/assets/models.d.ts +60725 -0
  104. package/script/src/assets/models.d.ts.map +1 -0
  105. package/script/src/assets/models.js +71917 -0
  106. package/script/src/helpers/async.d.ts.map +1 -0
  107. package/script/src/{async.js → helpers/async.js} +1 -1
  108. package/script/src/helpers/seedKeywords.d.ts.map +1 -0
  109. package/script/src/helpers/urls.d.ts.map +1 -0
  110. package/script/src/helpers/utils.d.ts.map +1 -0
  111. package/script/src/llm.d.ts +35 -0
  112. package/script/src/llm.d.ts.map +1 -0
  113. package/script/src/llm.js +65 -0
  114. package/script/src/providers/google.d.ts +12 -0
  115. package/script/src/providers/google.d.ts.map +1 -0
  116. package/script/src/providers/google.js +148 -0
  117. package/script/src/providers/index.d.ts +13 -0
  118. package/script/src/providers/index.d.ts.map +1 -0
  119. package/script/src/providers/index.js +24 -0
  120. package/script/src/providers/openai.d.ts +12 -0
  121. package/script/src/providers/openai.d.ts.map +1 -0
  122. package/script/src/providers/openai.js +181 -0
  123. package/script/src/providers/pricing.d.ts +72 -0
  124. package/script/src/providers/pricing.d.ts.map +1 -0
  125. package/script/src/providers/pricing.js +97 -0
  126. package/script/src/providers/registry.d.ts +20 -0
  127. package/script/src/providers/registry.d.ts.map +1 -0
  128. package/script/src/providers/registry.js +39 -0
  129. package/script/src/providers/types.d.ts +49 -0
  130. package/script/src/providers/types.d.ts.map +1 -0
  131. package/script/src/providers/types.js +8 -0
  132. package/script/src/response.d.ts +74 -0
  133. package/script/src/response.d.ts.map +1 -0
  134. package/script/src/response.js +114 -0
  135. package/script/src/tool.d.ts +58 -0
  136. package/script/src/tool.d.ts.map +1 -0
  137. package/script/src/tool.js +95 -0
  138. package/script/src/tools/brands.js +6 -6
  139. package/script/src/tools/classifier.d.ts +80 -17
  140. package/script/src/tools/classifier.d.ts.map +1 -1
  141. package/script/src/tools/classifier.js +72 -85
  142. package/script/src/tools/entities.d.ts +23 -12
  143. package/script/src/tools/entities.d.ts.map +1 -1
  144. package/script/src/tools/entities.js +29 -51
  145. package/script/src/tools/funnel.js +7 -7
  146. package/script/src/tools/generic.d.ts +17 -4
  147. package/script/src/tools/generic.d.ts.map +1 -1
  148. package/script/src/tools/generic.js +39 -14
  149. package/script/src/tools/keywords.js +5 -5
  150. package/script/src/tools/personas.d.ts +49 -2
  151. package/script/src/tools/personas.d.ts.map +1 -1
  152. package/script/src/tools/personas.js +63 -36
  153. package/script/src/tools/scorer.d.ts +24 -6
  154. package/script/src/tools/scorer.d.ts.map +1 -1
  155. package/script/src/tools/scorer.js +28 -24
  156. package/script/src/tools/search.d.ts.map +1 -1
  157. package/script/src/tools/search.js +69 -9
  158. package/script/src/tools/sentiment.d.ts +30 -8
  159. package/script/src/tools/sentiment.d.ts.map +1 -1
  160. package/script/src/tools/sentiment.js +37 -30
  161. package/script/src/tools/sources.d.ts +5 -5
  162. package/script/src/tools/sources.d.ts.map +1 -1
  163. package/script/src/tools/sources.js +4 -5
  164. package/script/src/tools/topics.d.ts +44 -16
  165. package/script/src/tools/topics.d.ts.map +1 -1
  166. package/script/src/tools/topics.js +80 -72
  167. package/script/src/tools/translate.d.ts +22 -31
  168. package/script/src/tools/translate.d.ts.map +1 -1
  169. package/script/src/tools/translate.js +43 -40
  170. package/esm/src/async.d.ts.map +0 -1
  171. package/esm/src/models.d.ts +0 -18
  172. package/esm/src/models.d.ts.map +0 -1
  173. package/esm/src/models.js +0 -48
  174. package/esm/src/openai.d.ts +0 -17
  175. package/esm/src/openai.d.ts.map +0 -1
  176. package/esm/src/openai.js +0 -136
  177. package/esm/src/tools/seedKeywords.d.ts.map +0 -1
  178. package/esm/src/urls.d.ts.map +0 -1
  179. package/esm/src/utils.d.ts.map +0 -1
  180. package/script/src/async.d.ts.map +0 -1
  181. package/script/src/models.d.ts +0 -18
  182. package/script/src/models.d.ts.map +0 -1
  183. package/script/src/models.js +0 -52
  184. package/script/src/openai.d.ts +0 -17
  185. package/script/src/openai.d.ts.map +0 -1
  186. package/script/src/openai.js +0 -175
  187. package/script/src/tools/seedKeywords.d.ts.map +0 -1
  188. package/script/src/urls.d.ts.map +0 -1
  189. package/script/src/utils.d.ts.map +0 -1
  190. /package/esm/src/{async.d.ts → helpers/async.d.ts} +0 -0
  191. /package/esm/src/{tools → helpers}/seedKeywords.d.ts +0 -0
  192. /package/esm/src/{tools → helpers}/seedKeywords.js +0 -0
  193. /package/esm/src/{urls.d.ts → helpers/urls.d.ts} +0 -0
  194. /package/esm/src/{urls.js → helpers/urls.js} +0 -0
  195. /package/esm/src/{utils.d.ts → helpers/utils.d.ts} +0 -0
  196. /package/esm/src/{utils.js → helpers/utils.js} +0 -0
  197. /package/script/src/{async.d.ts → helpers/async.d.ts} +0 -0
  198. /package/script/src/{tools → helpers}/seedKeywords.d.ts +0 -0
  199. /package/script/src/{tools → helpers}/seedKeywords.js +0 -0
  200. /package/script/src/{urls.d.ts → helpers/urls.d.ts} +0 -0
  201. /package/script/src/{urls.js → helpers/urls.js} +0 -0
  202. /package/script/src/{utils.d.ts → helpers/utils.d.ts} +0 -0
  203. /package/script/src/{utils.js → helpers/utils.js} +0 -0
@@ -1,11 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractAspectBasedSentiments = extractAspectBasedSentiments;
4
- exports.extractABSForBrandBatch = extractABSForBrandBatch;
5
- const async_js_1 = require("../async.js");
6
- const openai_js_1 = require("../openai.js");
3
+ exports.ABSentimentsSchema = exports.ABSentimentSchema = exports.SentimentExtractor = void 0;
4
+ const tool_js_1 = require("../tool.js");
7
5
  const sentiment_schema_js_1 = require("../schemas/sentiment.schema.js");
8
- const utils_js_1 = require("../utils.js");
6
+ const utils_js_1 = require("../helpers/utils.js");
9
7
  const ABS_PROMPT_SYSTEM = (0, utils_js_1.dedent)(`
10
8
  You're an expert in Aspect-Based Sentiment Analysis. Your task involves identifying specific
11
9
  entities mentioned in a text (e.g. a person, product, service, or experience) and determining the
@@ -41,32 +39,41 @@ Return the entities and their sentiments with reasons from the following text se
41
39
  {text}
42
40
  `);
43
41
  /**
44
- * Extracts aspect-based sentiments from a text.
42
+ * A tool that extracts aspect-based sentiments from text.
45
43
  */
46
- async function extractAspectBasedSentiments(text, instructions = '', model = 'gpt-4.1-mini') {
47
- if (text == null || text.trim() === '') {
48
- return [];
44
+ class SentimentExtractor extends tool_js_1.Tool {
45
+ systemPrompt;
46
+ constructor(config = {}, modelConfig) {
47
+ super(modelConfig);
48
+ const { instructions = '', brand = null } = config;
49
+ const brandInstructions = brand
50
+ ? (0, utils_js_1.dedent)(`
51
+ When analyzing the text, pay special attention to any mentions of the brand "${brand.shortName}"
52
+ or its products/services (${brand.portfolio}). Ensure that any sentiments expressed toward this
53
+ brand or its offerings are accurately captured in your output. Respond in language code ${brand.language}.
54
+ `)
55
+ : '';
56
+ const combinedInstructions = [instructions, brandInstructions].filter(Boolean).join('\n\n');
57
+ this.systemPrompt = ABS_PROMPT_SYSTEM.replace('{instructions}', combinedInstructions);
49
58
  }
50
- const promptSystem = ABS_PROMPT_SYSTEM.replace('{instructions}', instructions);
51
- const promptUser = ABS_PROMPT_USER.replace('{text}', text);
52
- const conversation = [
53
- { role: 'system', content: promptSystem },
54
- { role: 'user', content: promptUser }
55
- ];
56
- const { parsed } = await (0, openai_js_1.askOpenAISafe)(conversation, model, sentiment_schema_js_1.ABSentimentsSchema);
57
- if (!parsed) {
58
- throw new Error('Failed to parse response from OpenAI');
59
+ schema() {
60
+ return sentiment_schema_js_1.ABSentimentsSchema;
61
+ }
62
+ prompt(text) {
63
+ const userPrompt = ABS_PROMPT_USER.replace('{text}', text ?? '');
64
+ return [
65
+ { role: 'system', content: this.systemPrompt },
66
+ { role: 'user', content: userPrompt }
67
+ ];
68
+ }
69
+ isEmpty(text) {
70
+ return text == null || text.trim() === '';
71
+ }
72
+ extractResult(parsed) {
73
+ return parsed.aspects;
59
74
  }
60
- return parsed.aspects;
61
- }
62
- /**
63
- * Classifies multiple data records concurrently while preserving order.
64
- */
65
- async function extractABSForBrandBatch(texts, brand = null, model = 'gpt-4.1-mini', maxConcurrency = 100) {
66
- const instructions = brand ? (0, utils_js_1.dedent)(`
67
- When analyzing the text, pay special attention to any mentions of the brand "${brand.shortName}"
68
- or its products/services (${brand.portfolio}). Ensure that any sentiments expressed toward this
69
- brand or its offerings are accurately captured in your output. Respond in language code ${brand.language}.
70
- `) : '';
71
- return (0, async_js_1.mapParallel)(texts, maxConcurrency, (text) => extractAspectBasedSentiments(text, instructions, model));
72
75
  }
76
+ exports.SentimentExtractor = SentimentExtractor;
77
+ var sentiment_schema_js_2 = require("../schemas/sentiment.schema.js");
78
+ Object.defineProperty(exports, "ABSentimentSchema", { enumerable: true, get: function () { return sentiment_schema_js_2.ABSentimentSchema; } });
79
+ Object.defineProperty(exports, "ABSentimentsSchema", { enumerable: true, get: function () { return sentiment_schema_js_2.ABSentimentsSchema; } });
@@ -1,7 +1,7 @@
1
- import { type FlaggedBrand } from '../schemas/brand.schema.js';
1
+ import type { FlaggedBrand } from '../schemas/brand.schema.js';
2
2
  import type { CategorizedSource, EnrichedSource, Source } from '../schemas/sources.schema.js';
3
3
  import { type TopicLabel } from './topics.js';
4
- import type { AIParams } from '../openai.js';
4
+ import type { ProviderParams } from '../llm.js';
5
5
  import type { Entity } from './entities.js';
6
6
  export type { Source, EnrichedSource, CategorizedSource } from '../schemas/sources.schema.js';
7
7
  /**
@@ -27,13 +27,13 @@ export declare function collectURLs(sourceLists: Array<Array<Source>>, domains?:
27
27
  /**
28
28
  * Classifies an array of URLs into categories based on WEB_TAXONOMY.
29
29
  */
30
- export declare function classifyURLs(urls: Array<string>, model?: string, modelParams?: AIParams, maxConcurrency?: number): Promise<Record<string, TopicLabel | null>>;
30
+ export declare function classifyURLs(urls: Array<string>, model?: string, modelParams?: ProviderParams, maxConcurrency?: number): Promise<Record<string, TopicLabel | null>>;
31
31
  /**
32
32
  * Creates a category mapper for sources by collecting unique URLs/domains and classifying them.
33
33
  */
34
- export declare function makeCategoryMapper(sourceLists: Array<Array<Source>>, domains?: boolean, model?: string, modelParams?: AIParams, maxConcurrency?: number): Promise<Record<string, TopicLabel | null>>;
34
+ export declare function makeCategoryMapper(sourceLists: Array<Array<Source>>, domains?: boolean, model?: string, modelParams?: ProviderParams, maxConcurrency?: number): Promise<Record<string, TopicLabel | null>>;
35
35
  /**
36
36
  * Categorizes sources by assigning topic and subtopic to each EnrichedSource in-place(!).
37
37
  */
38
- export declare function categorizeSources(sourceLists: Array<Array<EnrichedSource>>, domains?: boolean, model?: string, modelParams?: AIParams, maxConcurrency?: number): Promise<Array<Array<CategorizedSource>>>;
38
+ export declare function categorizeSources(sourceLists: Array<Array<EnrichedSource>>, domains?: boolean, model?: string, modelParams?: ProviderParams, maxConcurrency?: number): Promise<Array<Array<CategorizedSource>>>;
39
39
  //# sourceMappingURL=sources.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../../src/src/tools/sources.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAA4C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAE9F;;GAEG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,EAC3B,QAAQ,GAAE,KAAK,CAAC,MAAM,CAAM,GAC1B,cAAc,CAoEhB;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EACpC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,EAC3B,QAAQ,GAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAM,GACjC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAIvC;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,GAC5B;IAAE,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,CA8DjE;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GACnC;IAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAAC,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,CAc/E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAC1B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EACjC,OAAO,GAAE,OAAe,GACtB,KAAK,CAAC,MAAM,CAAC,CAgBf;AAiED;;GAEG;AACH,wBAAsB,YAAY,CACjC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,QAA4C,EACzD,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC,CAAC,CAa5C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EACjC,OAAO,GAAE,OAAe,EACxB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,QAA4C,EACzD,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC,CAAC,CAI5C;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACtC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,EACzC,OAAO,GAAE,OAAe,EACxB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,QAA4C,EACzD,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAiB1C"}
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../../src/src/tools/sources.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAE9F;;GAEG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,EAC3B,QAAQ,GAAE,KAAK,CAAC,MAAM,CAAM,GAC1B,cAAc,CAoEhB;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,EACpC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC,EAC3B,QAAQ,GAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAM,GACjC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAIvC;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,GAC5B;IAAE,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,CA8DjE;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GACnC;IAAE,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAAC,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,CAc/E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAC1B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EACjC,OAAO,GAAE,OAAe,GACtB,KAAK,CAAC,MAAM,CAAC,CAgBf;AA+DD;;GAEG;AACH,wBAAsB,YAAY,CACjC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,EACnB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,cAAkD,EAC/D,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC,CAAC,CAc5C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EACjC,OAAO,GAAE,OAAe,EACxB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,cAAkD,EAC/D,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC,CAAC,CAI5C;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACtC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,EACzC,OAAO,GAAE,OAAe,EACxB,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,cAAkD,EAC/D,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAiB1C"}
@@ -8,7 +8,6 @@ exports.collectURLs = collectURLs;
8
8
  exports.classifyURLs = classifyURLs;
9
9
  exports.makeCategoryMapper = makeCategoryMapper;
10
10
  exports.categorizeSources = categorizeSources;
11
- const async_js_1 = require("../async.js");
12
11
  const brands_js_1 = require("./brands.js");
13
12
  const topics_js_1 = require("./topics.js");
14
13
  /**
@@ -232,16 +231,16 @@ const WEB_TAXONOMY = {
232
231
  'Personal / Portfolio Site'
233
232
  ]
234
233
  };
235
- const WEB_LABEL_SCHEMA = (0, topics_js_1.createLabelSchema)({ topics: (0, topics_js_1.toTopics)(WEB_TAXONOMY) });
236
- const WEB_TAXONOMY_SERIALIZED = JSON.stringify(WEB_TAXONOMY, null, 2);
234
+ const WEB_TAXONOMY_TOPICS = (0, topics_js_1.toTopics)(WEB_TAXONOMY);
237
235
  /**
238
236
  * Classifies an array of URLs into categories based on WEB_TAXONOMY.
239
237
  */
240
238
  async function classifyURLs(urls, model = 'gpt-5.1', modelParams = { reasoning: { effort: 'none' } }, maxConcurrency = 100) {
241
- const labels = await (0, async_js_1.mapParallel)(urls, maxConcurrency, url => (0, topics_js_1.assignTopic)(url, WEB_TAXONOMY_SERIALIZED, WEB_LABEL_SCHEMA, model, modelParams));
239
+ const assigner = new topics_js_1.TopicAssigner({ taxonomy: WEB_TAXONOMY_TOPICS }, { model, modelParams, maxConcurrency });
240
+ const results = await assigner.batch(urls);
242
241
  const urlToCategory = {};
243
242
  urls.forEach((url, index) => {
244
- urlToCategory[url] = labels[index];
243
+ urlToCategory[url] = results.toArray()[index];
245
244
  });
246
245
  return urlToCategory;
247
246
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from '../../deps/jsr.io/@zod/zod/4.3.6/src/index.js';
2
- import { type AIParams } from '../openai.js';
2
+ import { Tool, type ModelConfig } from '../tool.js';
3
+ import type { Message } from '../llm.js';
3
4
  import { type TopicType, type TaxonomyType, type TopicLabel } from '../schemas/topics.schema.js';
4
5
  export type { TopicType, TaxonomyType, TopicLabel };
5
6
  /**
@@ -48,27 +49,54 @@ export declare function createLabelSchema(taxonomy: TaxonomyType): z.ZodObject<{
48
49
  [x: string]: string;
49
50
  }>;
50
51
  }, z.core.$strip>;
51
- /**
52
- * Assigns a topic and subtopic to a single text using an LLM call.
53
- */
54
- export declare function assignTopic(text: string | null, taxonomy: TaxonomyType | Array<TopicType> | string, labelSchema: z.ZodType<TopicLabel>, model?: string, modelParams?: AIParams): Promise<TopicLabel | null>;
55
- /**
56
- * Assigns topics to multiple texts concurrently while preserving order.
57
- */
58
- export declare function assignTopics(texts: Array<string | null>, taxonomy: TaxonomyType | Array<TopicType>, model?: string, modelParams?: AIParams, maxConcurrency?: number): Promise<Array<TopicLabel | null>>;
59
- export interface TopicExtractionOptions {
60
- records: Array<Record<string, unknown>>;
52
+ export interface TopicExtractorConfig {
53
+ /** Maximum number of top-level topics (default: 10) */
61
54
  nTopics?: number;
55
+ /** Maximum number of subtopics per topic (default: 5) */
62
56
  nSubtopics?: number;
57
+ /** Additional instructions */
63
58
  instructions?: string;
59
+ /** Maximum number of records to sample (default: 500) */
64
60
  maxSamples?: number;
65
- model?: string;
66
- modelParams?: AIParams;
67
- maxRetries?: number;
61
+ /** Language for topics (default: same as records) */
68
62
  language?: string;
69
63
  }
70
64
  /**
71
- * Extracts a topic hierarchy from an array of records using an LLM.
65
+ * A tool that extracts a topic taxonomy from a set of records.
72
66
  */
73
- export declare function extractTopics({ records, nTopics, nSubtopics, instructions, maxSamples, model, modelParams, maxRetries, language }: TopicExtractionOptions): Promise<TaxonomyType>;
67
+ export declare class TopicExtractor extends Tool<Array<Record<string, unknown>>, TaxonomyType, TaxonomyType> {
68
+ private readonly maxSamples;
69
+ private readonly promptTemplate;
70
+ constructor(config: TopicExtractorConfig | undefined, modelConfig: ModelConfig);
71
+ protected schema(): z.ZodObject<{
72
+ topics: z.ZodArray<z.ZodObject<{
73
+ topic: z.ZodString;
74
+ subtopics: z.ZodArray<z.ZodString>;
75
+ }, z.core.$strip>>;
76
+ }, z.core.$strip>;
77
+ protected prompt(records: Array<Record<string, unknown>>): string;
78
+ protected isEmpty(records: Array<Record<string, unknown>>): boolean;
79
+ /**
80
+ * Not supported. TopicExtractor is an aggregation operation that extracts
81
+ * a single taxonomy from many records. Use invoke() instead.
82
+ */
83
+ batch(): never;
84
+ }
85
+ /**
86
+ * Configuration for the TopicAssigner tool.
87
+ */
88
+ export interface TopicAssignerConfig {
89
+ taxonomy: TaxonomyType | Array<TopicType>;
90
+ }
91
+ /**
92
+ * A tool that assigns topic and subtopic labels to text based on a taxonomy.
93
+ */
94
+ export declare class TopicAssigner extends Tool<string | null, TopicLabel, TopicLabel> {
95
+ private readonly labelSchema;
96
+ private readonly systemPrompt;
97
+ constructor(config: TopicAssignerConfig, modelConfig: ModelConfig);
98
+ protected schema(): z.ZodType<TopicLabel, unknown, z.core.$ZodTypeInternals<TopicLabel, unknown>>;
99
+ protected prompt(text: string | null): Message[];
100
+ protected isEmpty(text: string | null): boolean;
101
+ }
74
102
  //# sourceMappingURL=topics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"topics.d.ts","sourceRoot":"","sources":["../../../src/src/tools/topics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,+CAA+C,CAAC;AAElE,OAAO,EAAsC,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEjF,OAAO,EAEN,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,MAAM,6BAA6B,CAAC;AAIrC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAuEpD;;;GAGG;AACH,eAAO,MAAM,KAAK;;;iBAShB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;iBAEvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;iBAEnB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAKpH;AACD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY;;;;;;;kBAmBvD;AA+CD;;GAEG;AACH,wBAAsB,WAAW,CAChC,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,MAAM,EAClD,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAClC,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,QAA4C,GACvD,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA2B5B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EAC3B,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,EACzC,KAAK,GAAE,MAAkB,EACzB,WAAW,GAAE,QAA4C,EACzD,cAAc,GAAE,MAAY,GAC1B,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAgBnC;AAED,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,EACnC,OAAO,EACP,OAAY,EACZ,UAAc,EACd,YAAiB,EACjB,UAAgB,EAChB,KAAiB,EACjB,WAAgB,EAChB,UAAc,EACd,QAA6C,EAC7C,EAAE,sBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAsChD"}
1
+ {"version":3,"file":"topics.d.ts","sourceRoot":"","sources":["../../../src/src/tools/topics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,+CAA+C,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAEN,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,MAAM,6BAA6B,CAAC;AAIrC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AA+EpD;;;GAGG;AACH,eAAO,MAAM,KAAK;;;iBAShB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;iBAEvB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;iBAEnB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAKpH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY;;;;;;;kBAmBvD;AA+BD,MAAM,WAAW,oBAAoB;IACpC,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,IAAI,CACvC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC9B,YAAY,EACZ,YAAY,CACZ;IACA,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,oBAAoB,YAAK,EAAE,WAAW,EAAE,WAAW;cAkBpD,MAAM;;;;;;IAIzB,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;cAQrC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO;IAI5E;;;OAGG;IACM,KAAK,IAAI,KAAK;CAOvB;AAyBD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC;IAC7E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAwB;IACpD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW;cAiB9C,MAAM;IAIzB,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE;cAQ7B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;CAGxD"}
@@ -1,17 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Taxonomy = exports.TaxonomyBase = exports.Topic = void 0;
3
+ exports.TopicAssigner = exports.TopicExtractor = exports.Taxonomy = exports.TaxonomyBase = exports.Topic = void 0;
4
4
  exports.toTopics = toTopics;
5
5
  exports.createLabelSchema = createLabelSchema;
6
- exports.assignTopic = assignTopic;
7
- exports.assignTopics = assignTopics;
8
- exports.extractTopics = extractTopics;
9
6
  /* eslint no-console: ["warn", { allow: ["log", "warn", "error"] }] */
10
7
  const index_js_1 = require("../../deps/jsr.io/@zod/zod/4.3.6/src/index.js");
11
- const async_js_1 = require("../async.js");
12
- const openai_js_1 = require("../openai.js");
8
+ const tool_js_1 = require("../tool.js");
13
9
  const topics_schema_js_1 = require("../schemas/topics.schema.js");
14
- const utils_js_1 = require("../utils.js");
10
+ const utils_js_1 = require("../helpers/utils.js");
11
+ // =============================================================================
12
+ // Helper Functions
13
+ // =============================================================================
15
14
  /**
16
15
  * Levenshtein distance implementation for string similarity validation
17
16
  */
@@ -69,6 +68,9 @@ function validateSubtopics(topic, subtopics) {
69
68
  }
70
69
  return errors;
71
70
  }
71
+ // =============================================================================
72
+ // Schemas
73
+ // =============================================================================
72
74
  /**
73
75
  * Topic schema with subtopic validation
74
76
  * Validates that subtopics are sufficiently distinct from each other and from the parent topic.
@@ -128,6 +130,9 @@ function createLabelSchema(taxonomy) {
128
130
  }
129
131
  });
130
132
  }
133
+ // =============================================================================
134
+ // TopicExtractor
135
+ // =============================================================================
131
136
  const TOPICS_PROMPT = (0, utils_js_1.dedent)(`
132
137
  # Instructions
133
138
 
@@ -152,6 +157,49 @@ The taxonomy should follow the MECE framework (Mutually Exclusive, Collectively
152
157
 
153
158
  {records}
154
159
  `);
160
+ /**
161
+ * A tool that extracts a topic taxonomy from a set of records.
162
+ */
163
+ class TopicExtractor extends tool_js_1.Tool {
164
+ maxSamples;
165
+ promptTemplate;
166
+ constructor(config = {}, modelConfig) {
167
+ super(modelConfig);
168
+ const { nTopics = 10, nSubtopics = 5, instructions = '', maxSamples = 500, language = 'The same language as the records' } = config;
169
+ this.maxSamples = maxSamples;
170
+ this.promptTemplate = TOPICS_PROMPT
171
+ .replace('{n_topics}', String(nTopics))
172
+ .replace('{n_subtopics}', String(nSubtopics))
173
+ .replace('{instructions}', instructions)
174
+ .replace('{language}', language);
175
+ }
176
+ schema() {
177
+ return exports.Taxonomy;
178
+ }
179
+ prompt(records) {
180
+ const sampledRecords = records.length > this.maxSamples
181
+ ? records.slice(0, this.maxSamples)
182
+ : records;
183
+ const formattedRecords = (0, utils_js_1.formatRecordsAttrWise)(sampledRecords);
184
+ return this.promptTemplate.replace('{records}', formattedRecords);
185
+ }
186
+ isEmpty(records) {
187
+ return !records || records.length === 0;
188
+ }
189
+ /**
190
+ * Not supported. TopicExtractor is an aggregation operation that extracts
191
+ * a single taxonomy from many records. Use invoke() instead.
192
+ */
193
+ batch() {
194
+ throw new Error('TopicExtractor.batch() is not supported. ' +
195
+ 'This tool extracts a single taxonomy from many records (aggregation). ' +
196
+ 'Use invoke() with all records instead.');
197
+ }
198
+ }
199
+ exports.TopicExtractor = TopicExtractor;
200
+ // =============================================================================
201
+ // TopicAssigner
202
+ // =============================================================================
155
203
  const LABEL_PROMPT_SYSTEM = (0, utils_js_1.dedent)(`
156
204
  You're task is to use the following hierarchy of topics and subtopics (in json format),
157
205
  to assign the correct topic and subtopic to each text in the input.
@@ -170,74 +218,34 @@ Assign the correct topic and subtopic to the following text.
170
218
  {text}
171
219
  `);
172
220
  /**
173
- * Assigns a topic and subtopic to a single text using an LLM call.
221
+ * A tool that assigns topic and subtopic labels to text based on a taxonomy.
174
222
  */
175
- async function assignTopic(text, taxonomy, labelSchema, model = 'gpt-5.1', modelParams = { reasoning: { effort: 'none' } }) {
176
- if (text == null || text.trim() === '') {
177
- return null;
178
- }
179
- const systemPrompt = LABEL_PROMPT_SYSTEM.replace('{taxonomy}', typeof taxonomy === 'string' ? taxonomy : JSON.stringify(taxonomy, null, 2));
180
- const userPrompt = LABEL_PROMPT_USER.replace('{text}', text);
181
- const prompts = [
182
- { role: 'system', content: systemPrompt },
183
- { role: 'user', content: userPrompt }
184
- ];
185
- try {
186
- const { parsed, error } = await (0, openai_js_1.askOpenAISafe)(prompts, model, labelSchema, modelParams);
187
- if (error != null || parsed == null) {
188
- return null;
189
- }
190
- return parsed;
223
+ class TopicAssigner extends tool_js_1.Tool {
224
+ labelSchema;
225
+ systemPrompt;
226
+ constructor(config, modelConfig) {
227
+ super(modelConfig);
228
+ const { taxonomy } = config;
229
+ // Normalize taxonomy to TaxonomyType
230
+ const normalizedTaxonomy = Array.isArray(taxonomy)
231
+ ? { topics: taxonomy }
232
+ : taxonomy;
233
+ // Build schema and system prompt once in constructor
234
+ this.labelSchema = createLabelSchema(normalizedTaxonomy);
235
+ this.systemPrompt = LABEL_PROMPT_SYSTEM.replace('{taxonomy}', JSON.stringify(normalizedTaxonomy, null, 2));
191
236
  }
192
- catch (error) {
193
- console.warn(`Failed to assign topic for text "${text.substring(0, 50)}...":`, error);
194
- return null;
237
+ schema() {
238
+ return this.labelSchema;
195
239
  }
196
- }
197
- /**
198
- * Assigns topics to multiple texts concurrently while preserving order.
199
- */
200
- function assignTopics(texts, taxonomy, model = 'gpt-5.1', modelParams = { reasoning: { effort: 'none' } }, maxConcurrency = 100) {
201
- // Precompute normalized taxonomy and label schema once for whole batch
202
- const normalizedTaxonomy = Array.isArray(taxonomy)
203
- ? { topics: taxonomy }
204
- : taxonomy;
205
- const labelSchema = createLabelSchema(normalizedTaxonomy);
206
- const serializedTaxonomy = JSON.stringify(normalizedTaxonomy, null, 2);
207
- return (0, async_js_1.mapParallel)(texts, maxConcurrency, text => assignTopic(text, serializedTaxonomy, labelSchema, model, modelParams));
208
- }
209
- /**
210
- * Extracts a topic hierarchy from an array of records using an LLM.
211
- */
212
- async function extractTopics({ records, nTopics = 10, nSubtopics = 5, instructions = '', maxSamples = 500, model = 'gpt-4.1', modelParams = {}, maxRetries = 8, language = 'The same language as the records' }) {
213
- if (!records || records.length === 0) {
214
- return { topics: [] };
215
- }
216
- const sampledRecords = records.length > maxSamples
217
- ? records.slice(0, maxSamples)
218
- : records;
219
- const formattedRecords = (0, utils_js_1.formatRecordsAttrWise)(sampledRecords);
220
- const prompt = TOPICS_PROMPT
221
- .replace('{n_topics}', String(nTopics))
222
- .replace('{n_subtopics}', String(nSubtopics))
223
- .replace('{instructions}', instructions)
224
- .replace('{records}', formattedRecords)
225
- .replace('{language}', language);
226
- const { parsed, output_text, error } = await (0, openai_js_1.askOpenAISafe)(prompt, model, exports.Taxonomy, modelParams, maxRetries, 'return');
227
- if (error != null) {
228
- if (output_text == null) {
229
- throw new Error('Failed to get response from OpenAI');
230
- }
231
- try {
232
- const taxonomy = JSON.parse(output_text);
233
- return exports.TaxonomyBase.parse(taxonomy);
234
- }
235
- catch (parseError) {
236
- throw new Error(`Failed to parse response even without validation: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
237
- }
240
+ prompt(text) {
241
+ const userPrompt = LABEL_PROMPT_USER.replace('{text}', text ?? '');
242
+ return [
243
+ { role: 'system', content: this.systemPrompt },
244
+ { role: 'user', content: userPrompt }
245
+ ];
238
246
  }
239
- if (parsed == null) {
240
- throw new Error('Failed to parse response from OpenAI');
247
+ isEmpty(text) {
248
+ return text == null || text.trim() === '';
241
249
  }
242
- return parsed;
243
250
  }
251
+ exports.TopicAssigner = TopicAssigner;
@@ -1,44 +1,35 @@
1
+ import { Tool, type ModelConfig } from '../tool.js';
1
2
  import type { BrandContext } from '../schemas/brand.schema.js';
2
- export interface translateParams {
3
- keyword: string;
3
+ export interface KeywordTranslatorConfig {
4
+ /** Brand context for disambiguation */
5
+ brandContext: BrandContext;
6
+ /** Language for the output prompt (default: 'en') */
4
7
  language?: string;
5
- model?: string;
8
+ /** Additional instructions */
6
9
  instructions?: string;
7
- brandContext: BrandContext;
8
10
  }
9
11
  /**
10
- * Translates a single Google search keyword into an equivalent LLM prompt.
12
+ * A tool that converts Google search keywords into natural language LLM prompts.
13
+ * Uses raw text mode (no schema) since the output is plain text.
11
14
  */
12
- export declare function translate({ keyword, language, model, brandContext, instructions }: translateParams): Promise<string>;
13
- /**
14
- * Translates multiple Google search keywords to LLM prompts concurrently while preserving order.
15
- */
16
- export interface translateBatchParams {
17
- keywords: Array<string>;
18
- language?: string;
19
- model?: string;
20
- maxConcurrency?: number;
21
- instructions?: string;
22
- brandContext: BrandContext;
15
+ export declare class KeywordTranslator extends Tool<string | null, string, string> {
16
+ private readonly promptTemplate;
17
+ constructor(config: KeywordTranslatorConfig, modelConfig: ModelConfig);
18
+ protected prompt(keyword: string | null): string;
19
+ protected isEmpty(keyword: string | null): boolean;
23
20
  }
24
- export declare function translateBatch(params: translateBatchParams): Promise<Array<string>>;
25
- export interface reverseTranslateParams {
26
- prompt: string;
21
+ export interface PromptToKeywordConfig {
22
+ /** Language for the output keyword (default: 'en') */
27
23
  language?: string;
28
- model?: string;
29
24
  }
30
25
  /**
31
- * Converts a natural language prompt into an equivalent Google search keyword.
26
+ * A tool that converts natural language prompts into Google search keywords.
27
+ * Uses raw text mode (no schema) since the output is plain text.
32
28
  */
33
- export declare function reverseTranslate({ prompt, language, model }: reverseTranslateParams): Promise<string>;
34
- export interface reverseTranslateBatchParams {
35
- prompts: Array<string>;
36
- language?: string;
37
- model?: string;
38
- maxConcurrency?: number;
29
+ export declare class PromptToKeyword extends Tool<string | null, string, string> {
30
+ private readonly promptTemplate;
31
+ constructor(config: PromptToKeywordConfig | undefined, modelConfig: ModelConfig);
32
+ protected prompt(text: string | null): string;
33
+ protected isEmpty(text: string | null): boolean;
39
34
  }
40
- /**
41
- * Converts multiple natural language prompts to Google search keywords concurrently while preserving order.
42
- */
43
- export declare function reverseTranslateBatch(params: reverseTranslateBatchParams): Promise<Array<string>>;
44
35
  //# sourceMappingURL=translate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/src/tools/translate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AA+C/D,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC9B,EAAE,OAAO,EAAE,QAAe,EAAE,KAAoB,EAAE,YAAY,EAAE,YAAiB,EAAE,EAAE,eAAe,GAClG,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;GAEG;AAEH,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,YAAY,CAAC;CAC3B;AAED,wBAAsB,cAAc,CACnC,MAAM,EAAE,oBAAoB,GAC1B,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAQxB;AA+BD,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACrC,EAAE,MAAM,EAAE,QAAe,EAAE,KAAsB,EAAE,EAAE,sBAAsB,GACzE,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAC1C,MAAM,EAAE,2BAA2B,GACjC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAQxB"}
1
+ {"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/src/tools/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AA+C/D,MAAM,WAAW,uBAAuB;IACvC,uCAAuC;IACvC,YAAY,EAAE,YAAY,CAAC;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC;IACzE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,uBAAuB,EAAE,WAAW,EAAE,WAAW;IAarE,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;cAIpB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;CAG3D;AAmCD,MAAM,WAAW,qBAAqB;IACrC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC;IACvE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,qBAAqB,YAAK,EAAE,WAAW,EAAE,WAAW;IASxE,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;cAIjB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;CAGxD"}
@@ -1,12 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.translate = translate;
4
- exports.translateBatch = translateBatch;
5
- exports.reverseTranslate = reverseTranslate;
6
- exports.reverseTranslateBatch = reverseTranslateBatch;
7
- const async_js_1 = require("../async.js");
8
- const openai_js_1 = require("../openai.js");
9
- const utils_js_1 = require("../utils.js");
3
+ exports.PromptToKeyword = exports.KeywordTranslator = void 0;
4
+ const tool_js_1 = require("../tool.js");
5
+ const utils_js_1 = require("../helpers/utils.js");
10
6
  const TRANSLATE_PROMPT = (0, utils_js_1.dedent)(`
11
7
  You are an expert in understanding search intent and conversational AI interactions.
12
8
 
@@ -39,7 +35,7 @@ You will receive brand context (sector, country). Follow these rules strictly:
39
35
  - WRONG: "Tell me about geography" (ignores sector context)
40
36
  - CORRECT: "What is GEO in AI visibility and search optimization?" (uses sector to disambiguate the acronym)
41
37
 
42
- {additional_instructions}
38
+ {instructions}
43
39
 
44
40
  # Keyword
45
41
 
@@ -52,25 +48,32 @@ You will receive brand context (sector, country). Follow these rules strictly:
52
48
  Return only the converted prompt, nothing else.
53
49
  `);
54
50
  /**
55
- * Translates a single Google search keyword into an equivalent LLM prompt.
51
+ * A tool that converts Google search keywords into natural language LLM prompts.
52
+ * Uses raw text mode (no schema) since the output is plain text.
56
53
  */
57
- async function translate({ keyword, language = 'en', model = 'gpt-5-mini', brandContext, instructions = '' }) {
58
- const prompt = TRANSLATE_PROMPT
59
- .replace('{keyword}', keyword)
60
- .replace('{language}', language)
61
- .replace('{sector}', brandContext.sector)
62
- .replace('{country}', brandContext.country)
63
- .replace('{additional_instructions}', instructions);
64
- const { parsed } = await (0, openai_js_1.askOpenAISafe)(prompt, model);
65
- if (!parsed) {
66
- throw new Error('Failed to parse translation from OpenAI response');
54
+ class KeywordTranslator extends tool_js_1.Tool {
55
+ promptTemplate;
56
+ constructor(config, modelConfig) {
57
+ super(modelConfig);
58
+ const { brandContext, language = 'en', instructions = '' } = config;
59
+ this.promptTemplate = TRANSLATE_PROMPT
60
+ .replace('{language}', language)
61
+ .replace('{sector}', brandContext.sector)
62
+ .replace('{country}', brandContext.country)
63
+ .replace('{instructions}', instructions);
64
+ }
65
+ // No schema() override - uses default null for raw text mode
66
+ prompt(keyword) {
67
+ return this.promptTemplate.replace('{keyword}', keyword ?? '');
68
+ }
69
+ isEmpty(keyword) {
70
+ return keyword == null || keyword.trim() === '';
67
71
  }
68
- return parsed;
69
- }
70
- async function translateBatch(params) {
71
- const { keywords, language = 'en', model = 'gpt-4.1-mini', maxConcurrency = 100, brandContext, instructions = '' } = params;
72
- return (0, async_js_1.mapParallel)(keywords, maxConcurrency, kwd => translate({ keyword: kwd, language, model, brandContext, instructions }));
73
72
  }
73
+ exports.KeywordTranslator = KeywordTranslator;
74
+ // =============================================================================
75
+ // PromptToKeyword (LLM prompt → keyword)
76
+ // =============================================================================
74
77
  const REVERSE_TRANSLATE_PROMPT = (0, utils_js_1.dedent)(`
75
78
  You are an expert in understanding search intent.
76
79
 
@@ -100,22 +103,22 @@ Otherwise, Google keyword planner will not accept it.
100
103
  Return only the keyword, nothing else.
101
104
  `);
102
105
  /**
103
- * Converts a natural language prompt into an equivalent Google search keyword.
106
+ * A tool that converts natural language prompts into Google search keywords.
107
+ * Uses raw text mode (no schema) since the output is plain text.
104
108
  */
105
- async function reverseTranslate({ prompt, language = 'en', model = 'gpt-4.1-mini' }) {
106
- const requestPrompt = REVERSE_TRANSLATE_PROMPT
107
- .replace('{prompt}', prompt)
108
- .replace('{language}', language);
109
- const { parsed } = await (0, openai_js_1.askOpenAISafe)(requestPrompt, model);
110
- if (!parsed) {
111
- throw new Error('Failed to parse reverse translation from OpenAI response');
109
+ class PromptToKeyword extends tool_js_1.Tool {
110
+ promptTemplate;
111
+ constructor(config = {}, modelConfig) {
112
+ super(modelConfig);
113
+ const { language = 'en' } = config;
114
+ this.promptTemplate = REVERSE_TRANSLATE_PROMPT.replace('{language}', language);
115
+ }
116
+ // No schema() override - uses default null for raw text mode
117
+ prompt(text) {
118
+ return this.promptTemplate.replace('{prompt}', text ?? '');
119
+ }
120
+ isEmpty(text) {
121
+ return text == null || text.trim() === '';
112
122
  }
113
- return parsed;
114
- }
115
- /**
116
- * Converts multiple natural language prompts to Google search keywords concurrently while preserving order.
117
- */
118
- async function reverseTranslateBatch(params) {
119
- const { prompts, language = 'en', model = 'gpt-5-mini', maxConcurrency = 100 } = params;
120
- return (0, async_js_1.mapParallel)(prompts, maxConcurrency, p => reverseTranslate({ prompt: p, language, model }));
121
123
  }
124
+ exports.PromptToKeyword = PromptToKeyword;