-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.js
More file actions
116 lines (101 loc) · 3.38 KB
/
Copy pathclient.js
File metadata and controls
116 lines (101 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* HTTP Client for OriginSelect Discovery API.
* Sends requests to the backend with structured intent (zero LLM cost path).
*/
const API_BASE = process.env.API_BASE_URL || 'https://api.originselect.com';
/**
* Call POST /api/ai/discover with structured intent bypass.
* Passes structuredIntent directly — the backend skips the LLM intent parser.
*/
export async function discoverProducts(params) {
const {
query, country, category, values, brand, keywords,
priceMax, priceMin, market, limit, brandsLimit, collectionsLimit,
classification, intentType
} = params;
const body = {
// If query is provided, include it (useful for NL fallback).
// The structuredIntent takes precedence and skips LLM.
query: query || buildQueryLabel(params),
market: market || 'all',
limit: limit || 12,
brandsLimit: brandsLimit ?? 5,
collectionsLimit: collectionsLimit ?? 3,
// ── Structured intent bypass (zero LLM cost) ──
structuredIntent: {
intentType: intentType || 'product',
country: country || null,
category: category || null,
values: values || [],
keywords: keywords || [],
brand: brand || null,
priceMin: priceMin || null,
priceMax: priceMax || null,
classification: classification || null,
excludes: [],
subcategory: null,
sourceBrand: null,
sourceProduct: null,
industry: null
}
};
const headers = {
'Content-Type': 'application/json',
'User-Agent': 'OriginSelect-MCP/1.0'
};
const response = await fetch(`${API_BASE}/api/ai/discover`, {
method: 'POST',
headers,
body: JSON.stringify(body),
signal: AbortSignal.timeout(15000)
});
if (!response.ok) {
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(`API returned ${response.status}: ${errorText}`);
}
return response.json();
}
/**
* Call POST /api/ai/refine with intent modifications.
* No LLM call — pure in-memory transformation on the backend.
*/
export async function refineSearch(params) {
const { intent, modifications, market, limit, brandsLimit, collectionsLimit } = params;
const body = {
intent,
modifications,
market: market || intent.market || 'all',
limit: limit || 12,
brandsLimit: brandsLimit ?? 5,
collectionsLimit: collectionsLimit ?? 3
};
const headers = {
'Content-Type': 'application/json',
'User-Agent': 'OriginSelect-MCP/1.0'
};
const response = await fetch(`${API_BASE}/api/ai/refine`, {
method: 'POST',
headers,
body: JSON.stringify(body),
signal: AbortSignal.timeout(15000)
});
if (!response.ok) {
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(`API returned ${response.status}: ${errorText}`);
}
return response.json();
}
/**
* Build a human-readable label from structured params.
* Used as the `query` field for logging/session purposes.
*/
function buildQueryLabel(params) {
const parts = [];
if (params.values?.length) parts.push(params.values.join(', '));
if (params.category) parts.push(params.category);
if (params.keywords?.length) parts.push(params.keywords.join(' '));
if (params.brand) parts.push(`by ${params.brand}`);
if (params.country) parts.push(`from ${params.country}`);
if (params.priceMax) parts.push(`under $${params.priceMax}`);
return parts.join(' ') || 'product search';
}