@caoguo/maplibre-ai 0.0.2 → 0.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/llm/deepseek.ts"],"names":[],"mappings":"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","file":"chunk-ZI6ZF26Q.js","sourcesContent":["/**\n * DeepSeek LLM 客户端(PRD phase-0 §5.5 / §5.7 的 v1 后端能力)\n *\n * DeepSeek API 与 OpenAI Chat Completions 协议兼容:\n * base_url: https://api.deepseek.com\n * 模型: deepseek-chat(通用对话)/ deepseek-reasoner(深度推理)\n * 鉴权: Authorization: Bearer <API_KEY>\n *\n * 设计:\n * - 依赖注入的 fetch(浏览器/Node 均可运行,Node 18+ 原生 fetch)\n * - 自动重试(指数退避)\n * - 失败降级(可传入 fallback,规则引擎兜底)\n * - 支持流式(onChunk 回调)与 JSON 输出\n */\n\nexport interface DeepSeekConfig {\n /** API Key */\n apiKey: string;\n /** 基础地址,默认 https://api.deepseek.com */\n baseUrl?: string;\n /** 模型,默认 deepseek-chat */\n model?: 'deepseek-chat' | 'deepseek-reasoner' | (string & {});\n /** 温度 0-2 */\n temperature?: number;\n /** 最大 token */\n maxTokens?: number;\n /** 超时(ms),默认 30s */\n timeoutMs?: number;\n /** 重试次数,默认 2 */\n retries?: number;\n /** 注入 fetch(测试/Node 环境),默认 globalThis.fetch */\n fetchImpl?: typeof fetch;\n}\n\nexport interface ChatMessage {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\nexport interface ChatOptions {\n /** 是否启用 JSON 模式(要求模型返回纯 JSON) */\n json?: boolean;\n /** 流式回调 */\n onChunk?: (delta: string) => void;\n}\n\nexport interface ChatResult {\n /** 完整回复文本 */\n content: string;\n /** 消耗 token */\n usage?: { promptTokens: number; completionTokens: number; totalTokens: number };\n /** 使用的模型 */\n model: string;\n}\n\n/** 已解析的 JSON 回复 */\nexport interface JsonChatResult<T> {\n /** 解析出的 JSON 对象 */\n data: T;\n /** 原始文本 */\n raw: string;\n model: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.deepseek.com';\nconst DEFAULT_MODEL = 'deepseek-chat';\n\n/** 把中文异常文案简化为错误码 */\nfunction normalizeError(body: string): Error {\n const err = new Error(body || 'DeepSeek API 请求失败');\n err.name = 'DeepSeekError';\n return err;\n}\n\nexport class DeepSeekClient {\n private config: Required<Omit<DeepSeekConfig, 'fetchImpl'>> & { fetchImpl: typeof fetch };\n private lastRequestId = 0;\n\n constructor(config: DeepSeekConfig) {\n if (!config.apiKey) throw new Error('DeepSeekClient: apiKey 不能为空');\n this.config = {\n apiKey: config.apiKey,\n baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, ''),\n model: config.model ?? DEFAULT_MODEL,\n temperature: config.temperature ?? 0.3,\n maxTokens: config.maxTokens ?? 2048,\n timeoutMs: config.timeoutMs ?? 30000,\n retries: config.retries ?? 2,\n fetchImpl: config.fetchImpl ?? ((globalThis as { fetch?: typeof fetch }).fetch ?? fetch),\n };\n }\n\n /** 发起一次聊天补全 */\n async chat(messages: ChatMessage[], opts: ChatOptions = {}): Promise<ChatResult> {\n return this.requestWithRetry(messages, opts);\n }\n\n /** 聊天 + JSON 解析 */\n async chatJson<T>(messages: ChatMessage[]): Promise<JsonChatResult<T>> {\n const result = await this.chat(messages, { json: true });\n const raw = result.content.trim();\n // 去除可能的 ```json 围栏\n const cleaned = raw.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/, '');\n let data: T;\n try {\n data = JSON.parse(cleaned) as T;\n } catch (e) {\n // 尝试从文本中提取首个 JSON 对象\n const match = cleaned.match(/\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\]/);\n if (match) {\n data = JSON.parse(match[0]) as T;\n } else {\n throw new Error(`DeepSeek 返回了非 JSON 内容: ${raw.slice(0, 200)}`);\n }\n }\n return { data, raw, model: result.model };\n }\n\n private async requestWithRetry(messages: ChatMessage[], opts: ChatOptions): Promise<ChatResult> {\n let lastError: Error | null = null;\n for (let attempt = 0; attempt <= this.config.retries; attempt++) {\n try {\n return await this.requestOnce(messages, opts);\n } catch (e) {\n lastError = e as Error;\n // 仅对可重试错误(网络/429/5xx)重试\n const retryable = !(e instanceof Error) || !e.name.includes('DeepSeek') || this.isRetryable(e as Error);\n if (!retryable && !this.isRetryable(e as Error)) break;\n if (attempt < this.config.retries) {\n await this.sleep(Math.min(1000 * 2 ** attempt, 8000));\n }\n }\n }\n throw lastError ?? new Error('DeepSeek 请求失败');\n }\n\n private isRetryable(err: Error): boolean {\n const msg = err.message ?? '';\n return /429|500|502|503|504|timeout|network|ECONN|abort|fetch failed/i.test(msg);\n }\n\n private async requestOnce(messages: ChatMessage[], opts: ChatOptions): Promise<ChatResult> {\n const { fetchImpl } = this.config;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);\n\n const body: Record<string, unknown> = {\n model: this.config.model,\n messages,\n temperature: this.config.temperature,\n max_tokens: this.config.maxTokens,\n stream: Boolean(opts.onChunk),\n };\n if (opts.json) {\n body.response_format = { type: 'json_object' };\n }\n\n try {\n const res = await fetchImpl(`${this.config.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw normalizeError(`DeepSeek HTTP ${res.status}: ${text}`);\n }\n\n // 流式\n if (opts.onChunk && res.body) {\n return this.parseStream(res.body, opts.onChunk);\n }\n\n // 非流式\n const json = (await res.json()) as {\n choices: Array<{ message: { content: string } }>;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n model?: string;\n };\n const content = json.choices?.[0]?.message?.content ?? '';\n return {\n content,\n model: json.model ?? this.config.model,\n usage: json.usage\n ? { promptTokens: json.usage.prompt_tokens, completionTokens: json.usage.completion_tokens, totalTokens: json.usage.total_tokens }\n : undefined,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /** 解析 SSE 流式响应 */\n private async parseStream(\n body: ReadableStream<Uint8Array>,\n onChunk: (delta: string) => void\n ): Promise<ChatResult> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let content = '';\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('data:')) continue;\n const data = trimmed.slice(5).trim();\n if (data === '[DONE]') continue;\n try {\n const json = JSON.parse(data) as { choices?: Array<{ delta?: { content?: string } }> };\n const delta = json.choices?.[0]?.delta?.content ?? '';\n if (delta) {\n content += delta;\n onChunk(delta);\n }\n } catch {\n // 忽略无法解析的行\n }\n }\n }\n return { content, model: this.config.model };\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n }\n}\n\n/** 便捷工厂:创建默认配置的客户端 */\nexport function createDeepSeekClient(config: DeepSeekConfig): DeepSeekClient {\n return new DeepSeekClient(config);\n}\n"]}
@@ -0,0 +1,302 @@
1
+ 'use strict';
2
+
3
+ // src/copilot/copilot.ts
4
+ var PLACE_COORDINATES = {
5
+ \u6B66\u6C49: [114.305, 30.593],
6
+ \u5149\u8C37: [114.428, 30.507],
7
+ \u6C49\u53E3: [114.268, 30.586],
8
+ \u6B66\u660C: [114.316, 30.554],
9
+ \u5317\u4EAC: [116.407, 39.904],
10
+ \u4E0A\u6D77: [121.474, 31.23],
11
+ \u5E7F\u5DDE: [113.264, 23.129],
12
+ \u6DF1\u5733: [114.058, 22.543],
13
+ \u6210\u90FD: [104.067, 30.573],
14
+ \u676D\u5DDE: [120.155, 30.274],
15
+ \u5357\u4EAC: [118.796, 32.06],
16
+ \u91CD\u5E86: [106.551, 29.563],
17
+ \u897F\u5B89: [108.94, 34.341],
18
+ \u5929\u6D25: [117.2, 39.084]
19
+ };
20
+ var COLOR_NAMES = {
21
+ \u7EA2: "#ef4444",
22
+ \u7EA2\u8272: "#ef4444",
23
+ \u6A59: "#f97316",
24
+ \u6A59\u8272: "#f97316",
25
+ \u9EC4: "#f59e0b",
26
+ \u9EC4\u8272: "#f59e0b",
27
+ \u7EFF: "#22c55e",
28
+ \u7EFF\u8272: "#22c55e",
29
+ \u9752: "#22d3ee",
30
+ \u84DD\u8272: "#3b82f6",
31
+ \u84DD: "#3b82f6",
32
+ \u7D2B: "#8b5cf6",
33
+ \u7D2B\u8272: "#8b5cf6",
34
+ \u7C89: "#ec4899",
35
+ \u7070: "#6b7280",
36
+ \u9ED1\u8272: "#111827",
37
+ \u767D\u8272: "#ffffff"
38
+ };
39
+ var INTENT_PATTERNS = [
40
+ { intent: "popup_interaction", re: /弹窗|信息窗|弹出|popup|点击.*?显示|点击.*?弹出|点击.*?信息/, confidence: 0.92 },
41
+ { intent: "heatmap", re: /热力|heatmap|热度|密度图|热图/, confidence: 0.9 },
42
+ { intent: "add_line_polygon", re: /路线|连线|线段|画.{0,2}(线|面)|多边形|折线|面要素|LineString|polygon/, confidence: 0.88 },
43
+ { intent: "add_marker", re: /标记|marker|点标记|加点|标注|mark/, confidence: 0.86 },
44
+ { intent: "create_map", re: /地图|初始化|创建|生成|画布|渲染|map/, confidence: 0.8 }
45
+ ];
46
+ function classifyIntent(query) {
47
+ let best = { intent: "unknown", confidence: 0 };
48
+ for (const p of INTENT_PATTERNS) {
49
+ const m = query.match(p.re);
50
+ if (m) {
51
+ const c = p.confidence * (1 + 0.03 * Math.min(m[0].length / query.length, 1));
52
+ if (c > best.confidence) best = { intent: p.intent, confidence: Math.min(c, 1) };
53
+ }
54
+ }
55
+ return best;
56
+ }
57
+ function extractParams(query, intent) {
58
+ const params = {};
59
+ for (const [name, coord] of Object.entries(PLACE_COORDINATES)) {
60
+ if (query.includes(name)) {
61
+ params.place = name;
62
+ params.center = coord;
63
+ break;
64
+ }
65
+ }
66
+ const zoomMatch = query.match(/缩放\s*(\d{1,2})|zoom\s*(\d{1,2})|级别\s*(\d{1,2})/i);
67
+ if (zoomMatch) {
68
+ const z = parseInt(zoomMatch[1] ?? zoomMatch[2] ?? zoomMatch[3]);
69
+ if (!Number.isNaN(z) && z >= 0 && z <= 22) params.zoom = z;
70
+ }
71
+ if (/暗色|暗黑|深色|dark|夜间/.test(query)) params.style = "caoguo-dark";
72
+ else if (/亮色|浅色|白色|light|白天/.test(query)) params.style = "caoguo-light";
73
+ for (const [name, hex] of Object.entries(COLOR_NAMES)) {
74
+ if (query.includes(name)) {
75
+ params.color = hex;
76
+ break;
77
+ }
78
+ }
79
+ const sizeMatch = query.match(/(?:宽度|半径|大小|width|size)\s*(\d+)/i);
80
+ if (sizeMatch) params.size = parseInt(sizeMatch[1]);
81
+ const layerMatch = query.match(/(?:图层|layer)\s*["']?([\w-]+)["']?/i);
82
+ if (layerMatch) params.layerId = layerMatch[1];
83
+ const textMatch = query.match(/["「『]([^"」』]+)["」』]/);
84
+ if (textMatch) params.text = textMatch[1];
85
+ return params;
86
+ }
87
+ function generateCode(intent, p) {
88
+ const center = p.center ?? [114.305, 30.593];
89
+ const zoom = p.zoom ?? 12;
90
+ const style = p.style ?? "caoguo-dark";
91
+ const color = p.color ?? "#ef4444";
92
+ const size = p.size ?? 8;
93
+ const layerId = p.layerId ?? "my-layer";
94
+ switch (intent) {
95
+ case "create_map":
96
+ return [
97
+ `const map = new CaoguoMap.Map({`,
98
+ ` container: 'map',`,
99
+ ` center: [${center[0]}, ${center[1]}],`,
100
+ ` zoom: ${zoom},`,
101
+ ` style: '${style}',`,
102
+ `});`
103
+ ].join("\n");
104
+ case "add_marker":
105
+ return [
106
+ `map.addSource('marker-src', {`,
107
+ ` type: 'geojson',`,
108
+ ` data: { type: 'Feature', geometry: { type: 'Point', coordinates: [${center[0]}, ${center[1]}] }, properties: {} },`,
109
+ `});`,
110
+ `map.addLayer({`,
111
+ ` id: '${layerId}',`,
112
+ ` type: 'circle',`,
113
+ ` source: 'marker-src',`,
114
+ ` paint: { 'circle-radius': ${size}, 'circle-color': '${color}' },`,
115
+ `});`
116
+ ].join("\n");
117
+ case "add_line_polygon":
118
+ return [
119
+ `map.addSource('line-src', {`,
120
+ ` type: 'geojson',`,
121
+ ` data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [[${center[0]}, ${center[1]}], [${(center[0] + 0.05).toFixed(3)}, ${(center[1] + 0.03).toFixed(3)}]] }, properties: {} },`,
122
+ `});`,
123
+ `map.addLayer({`,
124
+ ` id: '${layerId}',`,
125
+ ` type: 'line',`,
126
+ ` source: 'line-src',`,
127
+ ` paint: { 'line-color': '${color}', 'line-width': ${Math.max(2, size)} },`,
128
+ `});`
129
+ ].join("\n");
130
+ case "heatmap":
131
+ return [
132
+ `map.addSource('heat-src', {`,
133
+ ` type: 'geojson',`,
134
+ ` data: { type: 'FeatureCollection', features: [] },`,
135
+ `});`,
136
+ `map.addLayer({`,
137
+ ` id: '${layerId}',`,
138
+ ` type: 'heatmap',`,
139
+ ` source: 'heat-src',`,
140
+ ` paint: {`,
141
+ ` 'heatmap-weight': 1,`,
142
+ ` 'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(33,102,172,0)', 0.5, '${color}', 1, 'rgb(239,68,68)'],`,
143
+ ` 'heatmap-radius': ${Math.max(20, size * 4)},`,
144
+ ` },`,
145
+ `});`
146
+ ].join("\n");
147
+ case "popup_interaction":
148
+ return [
149
+ `map.on('click', '${layerId}', (e) => {`,
150
+ ` const coords = e.features[0].geometry.coordinates.slice();`,
151
+ ` new maplibregl.Popup()`,
152
+ ` .setLngLat(coords)`,
153
+ ` .setHTML('${p.text ?? "\u8981\u7D20\u4FE1\u606F"}')`,
154
+ ` .addTo(map.instance);`,
155
+ `});`
156
+ ].join("\n");
157
+ default:
158
+ return `// \u65E0\u6CD5\u8BC6\u522B\u7684\u610F\u56FE\uFF1A${intent}`;
159
+ }
160
+ }
161
+ function generateFromQuery(query) {
162
+ const { intent, confidence } = classifyIntent(query);
163
+ const params = extractParams(query);
164
+ const code = generateCode(intent, params);
165
+ return {
166
+ intent,
167
+ params,
168
+ code,
169
+ confidence,
170
+ description: buildDescription(intent, params)
171
+ };
172
+ }
173
+ function buildDescription(intent, p) {
174
+ const parts = [];
175
+ switch (intent) {
176
+ case "create_map":
177
+ parts.push("\u521B\u5EFA\u57FA\u7840\u5730\u56FE");
178
+ break;
179
+ case "add_marker":
180
+ parts.push("\u6DFB\u52A0\u70B9\u6807\u8BB0");
181
+ break;
182
+ case "add_line_polygon":
183
+ parts.push("\u6DFB\u52A0\u7EBF/\u9762\u8981\u7D20");
184
+ break;
185
+ case "heatmap":
186
+ parts.push("\u751F\u6210\u70ED\u529B\u56FE");
187
+ break;
188
+ case "popup_interaction":
189
+ parts.push("\u7ED1\u5B9A\u70B9\u51FB\u5F39\u7A97");
190
+ break;
191
+ default:
192
+ parts.push("\u672A\u77E5\u610F\u56FE");
193
+ }
194
+ if (p.place) parts.push(`\u5730\u70B9:${p.place}`);
195
+ if (p.color) parts.push(`\u989C\u8272:${p.color}`);
196
+ return parts.join(" ");
197
+ }
198
+ var MapCopilot = class {
199
+ lastIntent = "create_map";
200
+ lastParams = {};
201
+ /** 生成代码(支持上下文增量修改) */
202
+ generate(query) {
203
+ const { intent, confidence } = classifyIntent(query);
204
+ const isModification = /改成|改为|换成|变成|调整为|设置成/.test(query);
205
+ if (isModification && this.lastIntent !== "unknown") {
206
+ const delta = extractParams(query, this.lastIntent);
207
+ this.lastParams = { ...this.lastParams, ...delta };
208
+ const code2 = generateCode(this.lastIntent, this.lastParams);
209
+ return {
210
+ intent: this.lastIntent,
211
+ params: { ...this.lastParams },
212
+ code: code2,
213
+ confidence,
214
+ description: buildDescription(this.lastIntent, this.lastParams)
215
+ };
216
+ }
217
+ this.lastIntent = intent;
218
+ this.lastParams = extractParams(query);
219
+ const code = generateCode(intent, this.lastParams);
220
+ return {
221
+ intent,
222
+ params: { ...this.lastParams },
223
+ code,
224
+ confidence,
225
+ description: buildDescription(intent, this.lastParams)
226
+ };
227
+ }
228
+ /** 重置上下文 */
229
+ reset() {
230
+ this.lastIntent = "create_map";
231
+ this.lastParams = {};
232
+ }
233
+ /** 获取当前上下文 */
234
+ get context() {
235
+ return { intent: this.lastIntent, params: { ...this.lastParams } };
236
+ }
237
+ };
238
+
239
+ // src/copilot/llmCopilot.ts
240
+ var SYSTEM_PROMPT = `\u4F60\u662F"\u8349\u679C\u5730\u56FE"(CaoguoMap) \u7684\u5730\u56FE\u4EE3\u7801\u751F\u6210\u52A9\u624B\u3002\u7528\u6237\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u5730\u56FE\u9700\u6C42\uFF0C\u4F60\u751F\u6210\u5BF9\u5E94\u7684 MapLibre GL JS \u4EE3\u7801\u7247\u6BB5\u3002
241
+
242
+ \u8349\u679C\u5730\u56FE API \u7EA6\u5B9A\uFF1A
243
+ - \u5730\u56FE\u5B9E\u4F8B\uFF1Anew CaoguoMap.Map({ container, center, zoom, style })
244
+ - \u6DFB\u52A0\u70B9\uFF1Amap.addSource + map.addLayer({ type: 'circle' })
245
+ - \u6DFB\u52A0\u7EBF\uFF1Amap.addLayer({ type: 'line' })
246
+ - \u70ED\u529B\u56FE\uFF1Amap.addLayer({ type: 'heatmap' })
247
+ - \u5F39\u7A97\uFF1Amap.on('click', layerId, cb) + new maplibregl.Popup()
248
+
249
+ \u8981\u6C42\uFF1A
250
+ 1. \u53EA\u8FD4\u56DE JSON\uFF0C\u683C\u5F0F\uFF1A{"intent":"create_map|add_marker|add_line_polygon|heatmap|popup_interaction","code":"...","description":"..."}
251
+ 2. code \u5FC5\u987B\u662F\u53EF\u76F4\u63A5\u8FD0\u884C\u7684 JavaScript \u4EE3\u7801\u7247\u6BB5\uFF08\u4E0D\u542B import\uFF09\uFF0C\u7981\u6B62\u5305\u542B\u4EFB\u4F55 fetch/\u7F51\u7EDC\u8BF7\u6C42/\u6587\u4EF6\u7CFB\u7EDF/\u5371\u9669\u64CD\u4F5C\u3002
252
+ 3. \u82E5\u9700\u6C42\u8D85\u51FA\u5730\u56FE\u4EE3\u7801\u8303\u7574\uFF0C\u8FD4\u56DE {"intent":"unknown","code":"","description":"\u65E0\u6CD5\u8BC6\u522B\u7684\u9700\u6C42"}\u3002
253
+ 4. \u4E0D\u8981\u8F93\u51FA JSON \u4EE5\u5916\u7684\u4EFB\u4F55\u6587\u5B57\u3002`;
254
+ var LlmMapCopilot = class {
255
+ client;
256
+ enabled;
257
+ constructor(config) {
258
+ this.client = config.client;
259
+ this.enabled = config.enabled ?? true;
260
+ }
261
+ /** 生成代码:LLM 优先,失败降级规则引擎 */
262
+ async generate(query) {
263
+ if (!this.enabled) {
264
+ return generateFromQuery(query);
265
+ }
266
+ try {
267
+ return await this.generateWithLlm(query);
268
+ } catch {
269
+ return generateFromQuery(query);
270
+ }
271
+ }
272
+ async generateWithLlm(query) {
273
+ const messages = [
274
+ { role: "system", content: SYSTEM_PROMPT },
275
+ { role: "user", content: query }
276
+ ];
277
+ const { data } = await this.client.chatJson(messages);
278
+ const intent = data.intent ?? "unknown";
279
+ const code = data.code ?? "";
280
+ if (intent === "unknown" || !code.trim()) {
281
+ return generateFromQuery(query);
282
+ }
283
+ return {
284
+ intent,
285
+ params: {},
286
+ code,
287
+ confidence: 0.9,
288
+ description: data.description ?? `LLM \u751F\u6210\uFF08${intent}\uFF09`
289
+ };
290
+ }
291
+ };
292
+
293
+ exports.COLOR_NAMES = COLOR_NAMES;
294
+ exports.LlmMapCopilot = LlmMapCopilot;
295
+ exports.MapCopilot = MapCopilot;
296
+ exports.PLACE_COORDINATES = PLACE_COORDINATES;
297
+ exports.classifyIntent = classifyIntent;
298
+ exports.extractParams = extractParams;
299
+ exports.generateCode = generateCode;
300
+ exports.generateFromQuery = generateFromQuery;
301
+ //# sourceMappingURL=index.cjs.map
302
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/copilot/copilot.ts","../../src/copilot/llmCopilot.ts"],"names":["code"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * MapCopilot v1(PRD phase-0 §5.5)\n *\n * 自然语言 → 草果地图 API 代码生成。\n *\n * v1 采用「意图识别 + 参数提取 + 模板生成」的规则引擎方案(不依赖大模型),\n * 覆盖 PRD 定义的 5 类核心交互意图:\n * I-1 create_map 创建基础地图\n * I-2 add_marker 添加点标记\n * I-3 add_line_polygon 添加线/面\n * I-4 heatmap 数据可视化(热力图)\n * I-5 popup_interaction 交互功能(点击弹窗)\n *\n * 全部为纯函数,可离线运行。支持上下文增量修改(\"把颜色改成红色\")。\n */\n\n// ============================================================\n// 一、类型定义\n// ============================================================\nexport type CopilotIntent =\n | 'create_map'\n | 'add_marker'\n | 'add_line_polygon'\n | 'heatmap'\n | 'popup_interaction'\n | 'unknown';\n\n/** 提取出的地图参数 */\nexport interface CopilotParams {\n /** 城市/地点名(用于映射中心坐标) */\n place?: string;\n /** 地图中心 [lng, lat] */\n center?: [number, number];\n /** 缩放级别 */\n zoom?: number;\n /** 主题样式 */\n style?: 'caoguo-dark' | 'caoguo-light';\n /** 要素颜色(hex) */\n color?: string;\n /** 线宽 / 半径 */\n size?: number;\n /** 图层 ID(用于交互意图) */\n layerId?: string;\n /** 文本内容(弹窗等) */\n text?: string;\n}\n\nexport interface CopilotResult {\n intent: CopilotIntent;\n params: CopilotParams;\n /** 生成的代码 */\n code: string;\n /** 识别置信度 0-1 */\n confidence: number;\n /** 可读说明 */\n description: string;\n}\n\n// ============================================================\n// 二、知识库(城市坐标 / 颜色 / 意图关键词)\n// ============================================================\n/** 常见城市/地点中心坐标(WGS84) */\nexport const PLACE_COORDINATES: Record<string, [number, number]> = {\n 武汉: [114.305, 30.593],\n 光谷: [114.428, 30.507],\n 汉口: [114.268, 30.586],\n 武昌: [114.316, 30.554],\n 北京: [116.407, 39.904],\n 上海: [121.474, 31.23],\n 广州: [113.264, 23.129],\n 深圳: [114.058, 22.543],\n 成都: [104.067, 30.573],\n 杭州: [120.155, 30.274],\n 南京: [118.796, 32.06],\n 重庆: [106.551, 29.563],\n 西安: [108.94, 34.341],\n 天津: [117.2, 39.084],\n};\n\n/** 颜色名 → hex */\nexport const COLOR_NAMES: Record<string, string> = {\n 红: '#ef4444',\n 红色: '#ef4444',\n 橙: '#f97316',\n 橙色: '#f97316',\n 黄: '#f59e0b',\n 黄色: '#f59e0b',\n 绿: '#22c55e',\n 绿色: '#22c55e',\n 青: '#22d3ee',\n 蓝色: '#3b82f6',\n 蓝: '#3b82f6',\n 紫: '#8b5cf6',\n 紫色: '#8b5cf6',\n 粉: '#ec4899',\n 灰: '#6b7280',\n 黑色: '#111827',\n 白色: '#ffffff',\n};\n\n/** 意图关键词(按优先级匹配) */\nconst INTENT_PATTERNS: Array<{ intent: CopilotIntent; re: RegExp; confidence: number }> = [\n { intent: 'popup_interaction', re: /弹窗|信息窗|弹出|popup|点击.*?显示|点击.*?弹出|点击.*?信息/, confidence: 0.92 },\n { intent: 'heatmap', re: /热力|heatmap|热度|密度图|热图/, confidence: 0.9 },\n { intent: 'add_line_polygon', re: /路线|连线|线段|画.{0,2}(线|面)|多边形|折线|面要素|LineString|polygon/, confidence: 0.88 },\n { intent: 'add_marker', re: /标记|marker|点标记|加点|标注|mark/, confidence: 0.86 },\n { intent: 'create_map', re: /地图|初始化|创建|生成|画布|渲染|map/, confidence: 0.8 },\n];\n\n// ============================================================\n// 三、意图识别\n// ============================================================\nexport function classifyIntent(query: string): { intent: CopilotIntent; confidence: number } {\n let best: { intent: CopilotIntent; confidence: number } = { intent: 'unknown', confidence: 0 };\n for (const p of INTENT_PATTERNS) {\n const m = query.match(p.re);\n if (m) {\n const c = p.confidence * (1 + 0.03 * Math.min(m[0].length / query.length, 1));\n if (c > best.confidence) best = { intent: p.intent, confidence: Math.min(c, 1) };\n }\n }\n return best;\n}\n\n// ============================================================\n// 四、参数提取\n// ============================================================\nexport function extractParams(query: string, intent: CopilotIntent): CopilotParams {\n const params: CopilotParams = {};\n\n // 地点 → 中心坐标\n for (const [name, coord] of Object.entries(PLACE_COORDINATES)) {\n if (query.includes(name)) {\n params.place = name;\n params.center = coord;\n break;\n }\n }\n\n // 缩放级别\n const zoomMatch = query.match(/缩放\\s*(\\d{1,2})|zoom\\s*(\\d{1,2})|级别\\s*(\\d{1,2})/i);\n if (zoomMatch) {\n const z = parseInt(zoomMatch[1] ?? zoomMatch[2] ?? zoomMatch[3]);\n if (!Number.isNaN(z) && z >= 0 && z <= 22) params.zoom = z;\n }\n\n // 主题\n if (/暗色|暗黑|深色|dark|夜间/.test(query)) params.style = 'caoguo-dark';\n else if (/亮色|浅色|白色|light|白天/.test(query)) params.style = 'caoguo-light';\n\n // 颜色\n for (const [name, hex] of Object.entries(COLOR_NAMES)) {\n if (query.includes(name)) {\n params.color = hex;\n break;\n }\n }\n\n // 尺寸(线宽/半径)\n const sizeMatch = query.match(/(?:宽度|半径|大小|width|size)\\s*(\\d+)/i);\n if (sizeMatch) params.size = parseInt(sizeMatch[1]);\n\n // 图层 ID(交互意图用)\n const layerMatch = query.match(/(?:图层|layer)\\s*[\"']?([\\w-]+)[\"']?/i);\n if (layerMatch) params.layerId = layerMatch[1];\n\n // 文本内容(弹窗用)\n const textMatch = query.match(/[\"「『]([^\"」』]+)[\"」』]/);\n if (textMatch) params.text = textMatch[1];\n\n return params;\n}\n\n// ============================================================\n// 五、代码模板生成\n// ============================================================\nexport function generateCode(intent: CopilotIntent, p: CopilotParams): string {\n const center = p.center ?? [114.305, 30.593];\n const zoom = p.zoom ?? 12;\n const style = p.style ?? 'caoguo-dark';\n const color = p.color ?? '#ef4444';\n const size = p.size ?? 8;\n const layerId = p.layerId ?? 'my-layer';\n\n switch (intent) {\n case 'create_map':\n return [\n `const map = new CaoguoMap.Map({`,\n ` container: 'map',`,\n ` center: [${center[0]}, ${center[1]}],`,\n ` zoom: ${zoom},`,\n ` style: '${style}',`,\n `});`,\n ].join('\\n');\n\n case 'add_marker':\n return [\n `map.addSource('marker-src', {`,\n ` type: 'geojson',`,\n ` data: { type: 'Feature', geometry: { type: 'Point', coordinates: [${center[0]}, ${center[1]}] }, properties: {} },`,\n `});`,\n `map.addLayer({`,\n ` id: '${layerId}',`,\n ` type: 'circle',`,\n ` source: 'marker-src',`,\n ` paint: { 'circle-radius': ${size}, 'circle-color': '${color}' },`,\n `});`,\n ].join('\\n');\n\n case 'add_line_polygon':\n return [\n `map.addSource('line-src', {`,\n ` type: 'geojson',`,\n ` data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [[${center[0]}, ${center[1]}], [${(center[0] + 0.05).toFixed(3)}, ${(center[1] + 0.03).toFixed(3)}]] }, properties: {} },`,\n `});`,\n `map.addLayer({`,\n ` id: '${layerId}',`,\n ` type: 'line',`,\n ` source: 'line-src',`,\n ` paint: { 'line-color': '${color}', 'line-width': ${Math.max(2, size)} },`,\n `});`,\n ].join('\\n');\n\n case 'heatmap':\n return [\n `map.addSource('heat-src', {`,\n ` type: 'geojson',`,\n ` data: { type: 'FeatureCollection', features: [] },`,\n `});`,\n `map.addLayer({`,\n ` id: '${layerId}',`,\n ` type: 'heatmap',`,\n ` source: 'heat-src',`,\n ` paint: {`,\n ` 'heatmap-weight': 1,`,\n ` 'heatmap-color': ['interpolate', ['linear'], ['heatmap-density'], 0, 'rgba(33,102,172,0)', 0.5, '${color}', 1, 'rgb(239,68,68)'],`,\n ` 'heatmap-radius': ${Math.max(20, size * 4)},`,\n ` },`,\n `});`,\n ].join('\\n');\n\n case 'popup_interaction':\n return [\n `map.on('click', '${layerId}', (e) => {`,\n ` const coords = e.features[0].geometry.coordinates.slice();`,\n ` new maplibregl.Popup()`,\n ` .setLngLat(coords)`,\n ` .setHTML('${p.text ?? '要素信息'}')`,\n ` .addTo(map.instance);`,\n `});`,\n ].join('\\n');\n\n default:\n return `// 无法识别的意图:${intent}`;\n }\n}\n\n// ============================================================\n// 六、MapCopilot 主入口(含上下文增量修改)\n// ============================================================\nexport function generateFromQuery(query: string): CopilotResult {\n const { intent, confidence } = classifyIntent(query);\n const params = extractParams(query, intent);\n const code = generateCode(intent, params);\n return {\n intent,\n params,\n code,\n confidence,\n description: buildDescription(intent, params),\n };\n}\n\nfunction buildDescription(intent: CopilotIntent, p: CopilotParams): string {\n const parts: string[] = [];\n switch (intent) {\n case 'create_map':\n parts.push('创建基础地图');\n break;\n case 'add_marker':\n parts.push('添加点标记');\n break;\n case 'add_line_polygon':\n parts.push('添加线/面要素');\n break;\n case 'heatmap':\n parts.push('生成热力图');\n break;\n case 'popup_interaction':\n parts.push('绑定点击弹窗');\n break;\n default:\n parts.push('未知意图');\n }\n if (p.place) parts.push(`地点:${p.place}`);\n if (p.color) parts.push(`颜色:${p.color}`);\n return parts.join(' ');\n}\n\n/**\n * MapCopilot 组件类:维护当前会话上下文,支持增量修改。\n *\n * 用法:\n * const copilot = new MapCopilot();\n * copilot.generate('创建一个武汉地图,暗色主题,缩放 12'); // → 代码\n * copilot.generate('把颜色改成红色'); // → 增量修改后代码\n */\nexport class MapCopilot {\n private lastIntent: CopilotIntent = 'create_map';\n private lastParams: CopilotParams = {};\n\n /** 生成代码(支持上下文增量修改) */\n generate(query: string): CopilotResult {\n const { intent, confidence } = classifyIntent(query);\n const isModification = /改成|改为|换成|变成|调整为|设置成/.test(query);\n\n if (isModification && this.lastIntent !== 'unknown') {\n // 增量修改:提取新参数,合并到上一轮参数\n const delta = extractParams(query, this.lastIntent);\n this.lastParams = { ...this.lastParams, ...delta };\n const code = generateCode(this.lastIntent, this.lastParams);\n return {\n intent: this.lastIntent,\n params: { ...this.lastParams },\n code,\n confidence,\n description: buildDescription(this.lastIntent, this.lastParams),\n };\n }\n\n // 全新意图\n this.lastIntent = intent;\n this.lastParams = extractParams(query, intent);\n const code = generateCode(intent, this.lastParams);\n return {\n intent,\n params: { ...this.lastParams },\n code,\n confidence,\n description: buildDescription(intent, this.lastParams),\n };\n }\n\n /** 重置上下文 */\n reset(): void {\n this.lastIntent = 'create_map';\n this.lastParams = {};\n }\n\n /** 获取当前上下文 */\n get context(): { intent: CopilotIntent; params: CopilotParams } {\n return { intent: this.lastIntent, params: { ...this.lastParams } };\n }\n}\n","/**\n * MapCopilot LLM 模式(PRD phase-0 §5.5 I-6「LLM 增强」)\n *\n * 在规则引擎(copilot.ts)基础上,接入 DeepSeek 大模型实现:\n * - 自然语言 → 地图代码生成的「自由意图」支持(突破 5 类模板限制)\n * - LLM 优先,失败自动降级到规则引擎\n *\n * 安全设计:\n * - 生成的代码片段仅返回文本,由上层决定是否执行(不在本模块 eval)\n * - 通过 system prompt 约束输出格式与 API 白名单\n */\n\nimport type { DeepSeekClient, ChatMessage } from '../llm/deepseek';\nimport { generateFromQuery, type CopilotResult, type CopilotIntent } from './copilot';\n\nexport interface LlmCopilotConfig {\n /** DeepSeek 客户端 */\n client: DeepSeekClient;\n /** 是否启用 LLM(false 则退化为纯规则引擎) */\n enabled?: boolean;\n}\n\n/** LLM 返回的代码生成结果结构 */\ninterface LlmCopilotOutput {\n intent?: string;\n code: string;\n description?: string;\n}\n\n/** 系统提示词:约束模型只生成草果地图 API 代码 */\nconst SYSTEM_PROMPT = `你是\"草果地图\"(CaoguoMap) 的地图代码生成助手。用户用自然语言描述地图需求,你生成对应的 MapLibre GL JS 代码片段。\n\n草果地图 API 约定:\n- 地图实例:new CaoguoMap.Map({ container, center, zoom, style })\n- 添加点:map.addSource + map.addLayer({ type: 'circle' })\n- 添加线:map.addLayer({ type: 'line' })\n- 热力图:map.addLayer({ type: 'heatmap' })\n- 弹窗:map.on('click', layerId, cb) + new maplibregl.Popup()\n\n要求:\n1. 只返回 JSON,格式:{\"intent\":\"create_map|add_marker|add_line_polygon|heatmap|popup_interaction\",\"code\":\"...\",\"description\":\"...\"}\n2. code 必须是可直接运行的 JavaScript 代码片段(不含 import),禁止包含任何 fetch/网络请求/文件系统/危险操作。\n3. 若需求超出地图代码范畴,返回 {\"intent\":\"unknown\",\"code\":\"\",\"description\":\"无法识别的需求\"}。\n4. 不要输出 JSON 以外的任何文字。`;\n\nexport class LlmMapCopilot {\n private client: DeepSeekClient;\n private enabled: boolean;\n\n constructor(config: LlmCopilotConfig) {\n this.client = config.client;\n this.enabled = config.enabled ?? true;\n }\n\n /** 生成代码:LLM 优先,失败降级规则引擎 */\n async generate(query: string): Promise<CopilotResult> {\n if (!this.enabled) {\n return generateFromQuery(query);\n }\n try {\n return await this.generateWithLlm(query);\n } catch {\n // LLM 失败降级到规则引擎\n return generateFromQuery(query);\n }\n }\n\n private async generateWithLlm(query: string): Promise<CopilotResult> {\n const messages: ChatMessage[] = [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: query },\n ];\n const { data } = await this.client.chatJson<LlmCopilotOutput>(messages);\n\n const intent = (data.intent ?? 'unknown') as CopilotIntent;\n const code = data.code ?? '';\n // 若 LLM 返回未知意图或空代码,降级规则引擎\n if (intent === 'unknown' || !code.trim()) {\n return generateFromQuery(query);\n }\n return {\n intent,\n params: {},\n code,\n confidence: 0.9,\n description: data.description ?? `LLM 生成(${intent})`,\n };\n }\n}\n"]}
@@ -0,0 +1,108 @@
1
+ import { DeepSeekClient } from '../llm/index.cjs';
2
+
3
+ /**
4
+ * MapCopilot v1(PRD phase-0 §5.5)
5
+ *
6
+ * 自然语言 → 草果地图 API 代码生成。
7
+ *
8
+ * v1 采用「意图识别 + 参数提取 + 模板生成」的规则引擎方案(不依赖大模型),
9
+ * 覆盖 PRD 定义的 5 类核心交互意图:
10
+ * I-1 create_map 创建基础地图
11
+ * I-2 add_marker 添加点标记
12
+ * I-3 add_line_polygon 添加线/面
13
+ * I-4 heatmap 数据可视化(热力图)
14
+ * I-5 popup_interaction 交互功能(点击弹窗)
15
+ *
16
+ * 全部为纯函数,可离线运行。支持上下文增量修改("把颜色改成红色")。
17
+ */
18
+ type CopilotIntent = 'create_map' | 'add_marker' | 'add_line_polygon' | 'heatmap' | 'popup_interaction' | 'unknown';
19
+ /** 提取出的地图参数 */
20
+ interface CopilotParams {
21
+ /** 城市/地点名(用于映射中心坐标) */
22
+ place?: string;
23
+ /** 地图中心 [lng, lat] */
24
+ center?: [number, number];
25
+ /** 缩放级别 */
26
+ zoom?: number;
27
+ /** 主题样式 */
28
+ style?: 'caoguo-dark' | 'caoguo-light';
29
+ /** 要素颜色(hex) */
30
+ color?: string;
31
+ /** 线宽 / 半径 */
32
+ size?: number;
33
+ /** 图层 ID(用于交互意图) */
34
+ layerId?: string;
35
+ /** 文本内容(弹窗等) */
36
+ text?: string;
37
+ }
38
+ interface CopilotResult {
39
+ intent: CopilotIntent;
40
+ params: CopilotParams;
41
+ /** 生成的代码 */
42
+ code: string;
43
+ /** 识别置信度 0-1 */
44
+ confidence: number;
45
+ /** 可读说明 */
46
+ description: string;
47
+ }
48
+ /** 常见城市/地点中心坐标(WGS84) */
49
+ declare const PLACE_COORDINATES: Record<string, [number, number]>;
50
+ /** 颜色名 → hex */
51
+ declare const COLOR_NAMES: Record<string, string>;
52
+ declare function classifyIntent(query: string): {
53
+ intent: CopilotIntent;
54
+ confidence: number;
55
+ };
56
+ declare function extractParams(query: string, intent: CopilotIntent): CopilotParams;
57
+ declare function generateCode(intent: CopilotIntent, p: CopilotParams): string;
58
+ declare function generateFromQuery(query: string): CopilotResult;
59
+ /**
60
+ * MapCopilot 组件类:维护当前会话上下文,支持增量修改。
61
+ *
62
+ * 用法:
63
+ * const copilot = new MapCopilot();
64
+ * copilot.generate('创建一个武汉地图,暗色主题,缩放 12'); // → 代码
65
+ * copilot.generate('把颜色改成红色'); // → 增量修改后代码
66
+ */
67
+ declare class MapCopilot {
68
+ private lastIntent;
69
+ private lastParams;
70
+ /** 生成代码(支持上下文增量修改) */
71
+ generate(query: string): CopilotResult;
72
+ /** 重置上下文 */
73
+ reset(): void;
74
+ /** 获取当前上下文 */
75
+ get context(): {
76
+ intent: CopilotIntent;
77
+ params: CopilotParams;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * MapCopilot LLM 模式(PRD phase-0 §5.5 I-6「LLM 增强」)
83
+ *
84
+ * 在规则引擎(copilot.ts)基础上,接入 DeepSeek 大模型实现:
85
+ * - 自然语言 → 地图代码生成的「自由意图」支持(突破 5 类模板限制)
86
+ * - LLM 优先,失败自动降级到规则引擎
87
+ *
88
+ * 安全设计:
89
+ * - 生成的代码片段仅返回文本,由上层决定是否执行(不在本模块 eval)
90
+ * - 通过 system prompt 约束输出格式与 API 白名单
91
+ */
92
+
93
+ interface LlmCopilotConfig {
94
+ /** DeepSeek 客户端 */
95
+ client: DeepSeekClient;
96
+ /** 是否启用 LLM(false 则退化为纯规则引擎) */
97
+ enabled?: boolean;
98
+ }
99
+ declare class LlmMapCopilot {
100
+ private client;
101
+ private enabled;
102
+ constructor(config: LlmCopilotConfig);
103
+ /** 生成代码:LLM 优先,失败降级规则引擎 */
104
+ generate(query: string): Promise<CopilotResult>;
105
+ private generateWithLlm;
106
+ }
107
+
108
+ export { COLOR_NAMES, type CopilotIntent, type CopilotParams, type CopilotResult, type LlmCopilotConfig, LlmMapCopilot, MapCopilot, PLACE_COORDINATES, classifyIntent, extractParams, generateCode, generateFromQuery };
@@ -0,0 +1,108 @@
1
+ import { DeepSeekClient } from '../llm/index.js';
2
+
3
+ /**
4
+ * MapCopilot v1(PRD phase-0 §5.5)
5
+ *
6
+ * 自然语言 → 草果地图 API 代码生成。
7
+ *
8
+ * v1 采用「意图识别 + 参数提取 + 模板生成」的规则引擎方案(不依赖大模型),
9
+ * 覆盖 PRD 定义的 5 类核心交互意图:
10
+ * I-1 create_map 创建基础地图
11
+ * I-2 add_marker 添加点标记
12
+ * I-3 add_line_polygon 添加线/面
13
+ * I-4 heatmap 数据可视化(热力图)
14
+ * I-5 popup_interaction 交互功能(点击弹窗)
15
+ *
16
+ * 全部为纯函数,可离线运行。支持上下文增量修改("把颜色改成红色")。
17
+ */
18
+ type CopilotIntent = 'create_map' | 'add_marker' | 'add_line_polygon' | 'heatmap' | 'popup_interaction' | 'unknown';
19
+ /** 提取出的地图参数 */
20
+ interface CopilotParams {
21
+ /** 城市/地点名(用于映射中心坐标) */
22
+ place?: string;
23
+ /** 地图中心 [lng, lat] */
24
+ center?: [number, number];
25
+ /** 缩放级别 */
26
+ zoom?: number;
27
+ /** 主题样式 */
28
+ style?: 'caoguo-dark' | 'caoguo-light';
29
+ /** 要素颜色(hex) */
30
+ color?: string;
31
+ /** 线宽 / 半径 */
32
+ size?: number;
33
+ /** 图层 ID(用于交互意图) */
34
+ layerId?: string;
35
+ /** 文本内容(弹窗等) */
36
+ text?: string;
37
+ }
38
+ interface CopilotResult {
39
+ intent: CopilotIntent;
40
+ params: CopilotParams;
41
+ /** 生成的代码 */
42
+ code: string;
43
+ /** 识别置信度 0-1 */
44
+ confidence: number;
45
+ /** 可读说明 */
46
+ description: string;
47
+ }
48
+ /** 常见城市/地点中心坐标(WGS84) */
49
+ declare const PLACE_COORDINATES: Record<string, [number, number]>;
50
+ /** 颜色名 → hex */
51
+ declare const COLOR_NAMES: Record<string, string>;
52
+ declare function classifyIntent(query: string): {
53
+ intent: CopilotIntent;
54
+ confidence: number;
55
+ };
56
+ declare function extractParams(query: string, intent: CopilotIntent): CopilotParams;
57
+ declare function generateCode(intent: CopilotIntent, p: CopilotParams): string;
58
+ declare function generateFromQuery(query: string): CopilotResult;
59
+ /**
60
+ * MapCopilot 组件类:维护当前会话上下文,支持增量修改。
61
+ *
62
+ * 用法:
63
+ * const copilot = new MapCopilot();
64
+ * copilot.generate('创建一个武汉地图,暗色主题,缩放 12'); // → 代码
65
+ * copilot.generate('把颜色改成红色'); // → 增量修改后代码
66
+ */
67
+ declare class MapCopilot {
68
+ private lastIntent;
69
+ private lastParams;
70
+ /** 生成代码(支持上下文增量修改) */
71
+ generate(query: string): CopilotResult;
72
+ /** 重置上下文 */
73
+ reset(): void;
74
+ /** 获取当前上下文 */
75
+ get context(): {
76
+ intent: CopilotIntent;
77
+ params: CopilotParams;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * MapCopilot LLM 模式(PRD phase-0 §5.5 I-6「LLM 增强」)
83
+ *
84
+ * 在规则引擎(copilot.ts)基础上,接入 DeepSeek 大模型实现:
85
+ * - 自然语言 → 地图代码生成的「自由意图」支持(突破 5 类模板限制)
86
+ * - LLM 优先,失败自动降级到规则引擎
87
+ *
88
+ * 安全设计:
89
+ * - 生成的代码片段仅返回文本,由上层决定是否执行(不在本模块 eval)
90
+ * - 通过 system prompt 约束输出格式与 API 白名单
91
+ */
92
+
93
+ interface LlmCopilotConfig {
94
+ /** DeepSeek 客户端 */
95
+ client: DeepSeekClient;
96
+ /** 是否启用 LLM(false 则退化为纯规则引擎) */
97
+ enabled?: boolean;
98
+ }
99
+ declare class LlmMapCopilot {
100
+ private client;
101
+ private enabled;
102
+ constructor(config: LlmCopilotConfig);
103
+ /** 生成代码:LLM 优先,失败降级规则引擎 */
104
+ generate(query: string): Promise<CopilotResult>;
105
+ private generateWithLlm;
106
+ }
107
+
108
+ export { COLOR_NAMES, type CopilotIntent, type CopilotParams, type CopilotResult, type LlmCopilotConfig, LlmMapCopilot, MapCopilot, PLACE_COORDINATES, classifyIntent, extractParams, generateCode, generateFromQuery };
@@ -0,0 +1,3 @@
1
+ export { COLOR_NAMES, LlmMapCopilot, MapCopilot, PLACE_COORDINATES, classifyIntent, extractParams, generateCode, generateFromQuery } from '../chunk-55G7QUFA.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}