@xapp/arachne-web-service 1.33.0 → 1.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/ArachneWebService.d.ts +36 -0
- package/lib/ArachneWebService.js +56 -9
- package/lib/ArachneWebService.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/llm/LLMProvider.d.ts +6 -2
- package/lib/llm/LLMProvider.js +5 -4
- package/lib/llm/LLMProvider.js.map +1 -1
- package/lib/services/ServiceExtractor.d.ts +195 -0
- package/lib/services/ServiceExtractor.js +541 -0
- package/lib/services/ServiceExtractor.js.map +1 -0
- package/lib/services/readServicePages.d.ts +37 -0
- package/lib/services/readServicePages.js +58 -0
- package/lib/services/readServicePages.js.map +1 -0
- package/lib/services/selectServicePages.d.ts +34 -0
- package/lib/services/selectServicePages.js +177 -0
- package/lib/services/selectServicePages.js.map +1 -0
- package/lib/taxonomy/ServiceTaxonomy.d.ts +5 -2
- package/lib/taxonomy/ServiceTaxonomy.js +10 -7
- package/lib/taxonomy/ServiceTaxonomy.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { LLMProvider } from "../llm/LLMProvider";
|
|
2
|
+
import { ServiceTaxonomy } from "../taxonomy/ServiceTaxonomy";
|
|
3
|
+
import { ServicePageKind } from "./selectServicePages";
|
|
4
|
+
/**
|
|
5
|
+
* Bumped whenever the prompt, schema or normalizer changes, so stored extractions can be compared by version.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EXTRACTOR_VERSION = "2026-10-30.2";
|
|
8
|
+
/**
|
|
9
|
+
* Default cap on the page text sent to the LLM, in tokens (estimated at four characters a token).
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_MAX_INPUT_TOKENS = 40000;
|
|
12
|
+
export interface ServicePageText {
|
|
13
|
+
url: string;
|
|
14
|
+
kind: ServicePageKind;
|
|
15
|
+
/**
|
|
16
|
+
* The page as markdown, navigation stripped.
|
|
17
|
+
*/
|
|
18
|
+
markdown: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Where a value was found: a page that was read, and text copied from it.
|
|
22
|
+
*/
|
|
23
|
+
export interface ServiceEvidence {
|
|
24
|
+
url: string;
|
|
25
|
+
snippet: string;
|
|
26
|
+
}
|
|
27
|
+
export interface EvidencedValue {
|
|
28
|
+
value: string;
|
|
29
|
+
evidence: ServiceEvidence[];
|
|
30
|
+
}
|
|
31
|
+
export interface ScoredValue extends EvidencedValue {
|
|
32
|
+
/**
|
|
33
|
+
* 0 to 1, as the model reported it.
|
|
34
|
+
*/
|
|
35
|
+
confidence: number;
|
|
36
|
+
}
|
|
37
|
+
export interface ExtractedService {
|
|
38
|
+
/**
|
|
39
|
+
* Canonical taxonomy job slug, or a family slug for a service the site advertises only in general terms.
|
|
40
|
+
*/
|
|
41
|
+
slug: string;
|
|
42
|
+
trade: string;
|
|
43
|
+
/**
|
|
44
|
+
* Undefined when the job sits directly under its trade.
|
|
45
|
+
*/
|
|
46
|
+
family?: string;
|
|
47
|
+
confidence: number;
|
|
48
|
+
evidence: ServiceEvidence[];
|
|
49
|
+
}
|
|
50
|
+
export interface ExtractedFlag {
|
|
51
|
+
/**
|
|
52
|
+
* False only when the site says so; a flag with no evidence either way is absent.
|
|
53
|
+
*/
|
|
54
|
+
value: boolean;
|
|
55
|
+
confidence: number;
|
|
56
|
+
evidence: ServiceEvidence[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A value the normalizer dropped.
|
|
60
|
+
*/
|
|
61
|
+
export interface RejectedValue {
|
|
62
|
+
field: string;
|
|
63
|
+
value: string;
|
|
64
|
+
reason: "no_evidence" | "homeowner_only" | "not_in_taxonomy";
|
|
65
|
+
/**
|
|
66
|
+
* The evidence the model gave for a value dropped for `no_evidence`, for auditing.
|
|
67
|
+
*/
|
|
68
|
+
evidence?: ServiceEvidence[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The services, capabilities and service area a business website advertises, in Service Taxonomy terms.
|
|
72
|
+
*
|
|
73
|
+
* Every value carries evidence copied from a page that was read; values without it are dropped and listed in
|
|
74
|
+
* `rejected`.
|
|
75
|
+
*/
|
|
76
|
+
export interface ServiceExtraction {
|
|
77
|
+
taxonomyVersion: string;
|
|
78
|
+
extractorVersion: string;
|
|
79
|
+
/**
|
|
80
|
+
* The LLM that read the pages.
|
|
81
|
+
*/
|
|
82
|
+
provider: string;
|
|
83
|
+
/**
|
|
84
|
+
* Undefined when the site is not a home-services business.
|
|
85
|
+
*/
|
|
86
|
+
primaryTrade?: ScoredValue;
|
|
87
|
+
secondaryTrades: ScoredValue[];
|
|
88
|
+
services: ExtractedService[];
|
|
89
|
+
/**
|
|
90
|
+
* Services the site offers that have no job in the taxonomy, as the site words them.
|
|
91
|
+
*/
|
|
92
|
+
unmappedServices: EvidencedValue[];
|
|
93
|
+
/**
|
|
94
|
+
* What the site says it does not do.
|
|
95
|
+
*/
|
|
96
|
+
explicitExclusions: EvidencedValue[];
|
|
97
|
+
/**
|
|
98
|
+
* Capability flags by slug.
|
|
99
|
+
*/
|
|
100
|
+
flags: Record<string, ExtractedFlag>;
|
|
101
|
+
certifications: EvidencedValue[];
|
|
102
|
+
equipmentTypes: EvidencedValue[];
|
|
103
|
+
specialtyMaterials: EvidencedValue[];
|
|
104
|
+
gear: EvidencedValue[];
|
|
105
|
+
subTrades: EvidencedValue[];
|
|
106
|
+
/**
|
|
107
|
+
* Financing or rebate programs named.
|
|
108
|
+
*/
|
|
109
|
+
programs: EvidencedValue[];
|
|
110
|
+
homeWarrantyNetworks: EvidencedValue[];
|
|
111
|
+
writtenReportsCertifications: EvidencedValue[];
|
|
112
|
+
languages: EvidencedValue[];
|
|
113
|
+
brandsServiced: EvidencedValue[];
|
|
114
|
+
serviceArea: {
|
|
115
|
+
cities: EvidencedValue[];
|
|
116
|
+
counties: EvidencedValue[];
|
|
117
|
+
zips: EvidencedValue[];
|
|
118
|
+
radiusMiles: EvidencedValue[];
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* The pages sent to the LLM, in order.
|
|
122
|
+
*/
|
|
123
|
+
pagesRead: string[];
|
|
124
|
+
rejected: RejectedValue[];
|
|
125
|
+
extractedAt: string;
|
|
126
|
+
}
|
|
127
|
+
export interface ExtractServicesCallOptions {
|
|
128
|
+
/**
|
|
129
|
+
* Trades the business is known to be in. Skips the classifier call; the first is the primary trade.
|
|
130
|
+
*/
|
|
131
|
+
trades?: string[];
|
|
132
|
+
/**
|
|
133
|
+
* Cap on the page text sent, in tokens. Default `DEFAULT_MAX_INPUT_TOKENS`.
|
|
134
|
+
*/
|
|
135
|
+
maxInputTokens?: number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Text as a reader sees it: markdown links, emphasis, escapes and heading underlines removed, quotes and dashes
|
|
139
|
+
* straightened, case and spacing folded.
|
|
140
|
+
*/
|
|
141
|
+
export declare function normalizeText(text: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* Reads the pages of a business website and returns the services it advertises in Service Taxonomy terms.
|
|
144
|
+
*
|
|
145
|
+
* LLM calls at temperature 0: a classifier picks the trades from the start of each page (skipped when the trades
|
|
146
|
+
* are given), then the extractor chooses from those trades' advertised jobs. When it leaves services unmapped, a
|
|
147
|
+
* third call matches them against the jobs of every trade, since a site often advertises work outside the trades
|
|
148
|
+
* it was classified in. Each answer must match its JSON schema or it is retried once. Values whose snippet is not
|
|
149
|
+
* on the page they cite are dropped.
|
|
150
|
+
*/
|
|
151
|
+
export declare class ServiceExtractor {
|
|
152
|
+
private readonly provider;
|
|
153
|
+
private readonly taxonomy;
|
|
154
|
+
constructor(props: {
|
|
155
|
+
provider: LLMProvider;
|
|
156
|
+
taxonomy?: ServiceTaxonomy;
|
|
157
|
+
});
|
|
158
|
+
/**
|
|
159
|
+
* @param pages The pages to read, most important first
|
|
160
|
+
* @returns The extraction, or undefined when no page has text or an LLM answer is unusable after a retry
|
|
161
|
+
*/
|
|
162
|
+
extract(pages: ServicePageText[], options?: ExtractServicesCallOptions): Promise<ServiceExtraction | undefined>;
|
|
163
|
+
private normalize;
|
|
164
|
+
/**
|
|
165
|
+
* Asks which job of any trade each unmapped service is, and moves the ones that match into services. A match in a
|
|
166
|
+
* trade the business was not classified in adds that trade to the secondary trades. When the answer is unusable
|
|
167
|
+
* the services stay unmapped.
|
|
168
|
+
*/
|
|
169
|
+
private matchUnmapped;
|
|
170
|
+
/**
|
|
171
|
+
* The job or family a slug names, following aliases and deprecations.
|
|
172
|
+
*/
|
|
173
|
+
private lookup;
|
|
174
|
+
private addService;
|
|
175
|
+
/**
|
|
176
|
+
* Keeps every page, trimming the longest ones until the total fits the budget.
|
|
177
|
+
*/
|
|
178
|
+
private fit;
|
|
179
|
+
private pagesBlock;
|
|
180
|
+
private classifyPrompt;
|
|
181
|
+
private classifySchema;
|
|
182
|
+
/**
|
|
183
|
+
* The slugs the extractor may choose for the trades: each family, for a service advertised in general terms,
|
|
184
|
+
* followed by its advertised jobs, then the jobs that sit directly under a trade.
|
|
185
|
+
*/
|
|
186
|
+
private catalog;
|
|
187
|
+
private catalogText;
|
|
188
|
+
private matchPrompt;
|
|
189
|
+
private extractPrompt;
|
|
190
|
+
private extractSchema;
|
|
191
|
+
/**
|
|
192
|
+
* Calls the LLM and checks the answer's shape, retrying once.
|
|
193
|
+
*/
|
|
194
|
+
private ask;
|
|
195
|
+
}
|