@xom11/whiteboard 0.24.1 → 0.24.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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/ai.d.mts +616 -0
  3. package/dist/ai.d.ts +616 -0
  4. package/dist/ai.js +1036 -0
  5. package/dist/ai.js.map +1 -0
  6. package/dist/ai.mjs +999 -0
  7. package/dist/ai.mjs.map +1 -0
  8. package/dist/catalog.json +4 -4
  9. package/dist/{chunk-4D5CSIJO.mjs → chunk-2WF6KIGF.mjs} +4 -3
  10. package/dist/chunk-2WF6KIGF.mjs.map +1 -0
  11. package/dist/{chunk-WWMQ2VHZ.mjs → chunk-45CGKJ7S.mjs} +4 -4
  12. package/dist/{chunk-WWMQ2VHZ.mjs.map → chunk-45CGKJ7S.mjs.map} +1 -1
  13. package/dist/{chunk-CRAPWQKJ.mjs → chunk-4DS3MKID.mjs} +4 -4
  14. package/dist/{chunk-CRAPWQKJ.mjs.map → chunk-4DS3MKID.mjs.map} +1 -1
  15. package/dist/chunk-73Q7ADVL.mjs +35 -0
  16. package/dist/chunk-73Q7ADVL.mjs.map +1 -0
  17. package/dist/{chunk-YIPI3WUL.mjs → chunk-7WQXXEVR.mjs} +4 -4
  18. package/dist/{chunk-YIPI3WUL.mjs.map → chunk-7WQXXEVR.mjs.map} +1 -1
  19. package/dist/{chunk-CSCF3YFZ.mjs → chunk-BEZSQKPY.mjs} +4 -3
  20. package/dist/chunk-BEZSQKPY.mjs.map +1 -0
  21. package/dist/{chunk-MFOGFFIL.mjs → chunk-CGZZO4BX.mjs} +5 -4
  22. package/dist/chunk-CGZZO4BX.mjs.map +1 -0
  23. package/dist/{chunk-IBTRMWD6.mjs → chunk-KRC2XOIG.mjs} +3 -3
  24. package/dist/{chunk-IBTRMWD6.mjs.map → chunk-KRC2XOIG.mjs.map} +1 -1
  25. package/dist/{chunk-6V4SH4JJ.mjs → chunk-WM2VDYQA.mjs} +4 -34
  26. package/dist/chunk-WM2VDYQA.mjs.map +1 -0
  27. package/dist/geometry-2d.d.mts +2 -1
  28. package/dist/geometry-2d.d.ts +2 -1
  29. package/dist/geometry-2d.mjs +5 -4
  30. package/dist/geometry-3d.d.mts +2 -1
  31. package/dist/geometry-3d.d.ts +2 -1
  32. package/dist/geometry-3d.mjs +4 -3
  33. package/dist/graph-2d.d.mts +2 -1
  34. package/dist/graph-2d.d.ts +2 -1
  35. package/dist/graph-2d.mjs +4 -3
  36. package/dist/{host-DOAYVL35.mjs → host-EPZCNFLH.mjs} +8 -7
  37. package/dist/host-EPZCNFLH.mjs.map +1 -0
  38. package/dist/{host-GKNQBBUE.mjs → host-LKCMYEAV.mjs} +7 -6
  39. package/dist/host-LKCMYEAV.mjs.map +1 -0
  40. package/dist/{host-TLIXN4CF.mjs → host-ZIQ77W33.mjs} +6 -5
  41. package/dist/host-ZIQ77W33.mjs.map +1 -0
  42. package/dist/index.d.mts +4 -616
  43. package/dist/index.d.ts +4 -616
  44. package/dist/index.js +28 -1028
  45. package/dist/index.js.map +1 -1
  46. package/dist/index.mjs +17 -1009
  47. package/dist/index.mjs.map +1 -1
  48. package/dist/latex.d.mts +2 -1
  49. package/dist/latex.d.ts +2 -1
  50. package/dist/serialize-JAVOU22E.mjs +7 -0
  51. package/dist/{serialize-3NZS6A6Q.mjs.map → serialize-JAVOU22E.mjs.map} +1 -1
  52. package/dist/{types-rA4slL08.d.mts → types-Crbefnfe.d.ts} +2 -49
  53. package/dist/types-DxlMPh-6.d.mts +49 -0
  54. package/dist/types-DxlMPh-6.d.ts +49 -0
  55. package/dist/{types-rA4slL08.d.ts → types-vtvyKGAA.d.mts} +2 -49
  56. package/package.json +6 -1
  57. package/dist/chunk-4D5CSIJO.mjs.map +0 -1
  58. package/dist/chunk-6V4SH4JJ.mjs.map +0 -1
  59. package/dist/chunk-CSCF3YFZ.mjs.map +0 -1
  60. package/dist/chunk-MFOGFFIL.mjs.map +0 -1
  61. package/dist/host-DOAYVL35.mjs.map +0 -1
  62. package/dist/host-GKNQBBUE.mjs.map +0 -1
  63. package/dist/host-TLIXN4CF.mjs.map +0 -1
  64. package/dist/serialize-3NZS6A6Q.mjs +0 -6
package/dist/ai.js ADDED
@@ -0,0 +1,1036 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var Anthropic = require('@anthropic-ai/sdk');
5
+ var zodToJsonSchema = require('zod-to-json-schema');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
10
+
11
+ // src/stamps/geometry-2d/dsl/schema.ts
12
+ var NameZ = zod.z.string().regex(/^[A-Za-z][A-Za-z0-9_'₀-₉]{0,11}$/);
13
+ var DslPoint = zod.z.discriminatedUnion("kind", [
14
+ zod.z.object({
15
+ name: NameZ,
16
+ kind: zod.z.literal("free"),
17
+ x: zod.z.number().finite(),
18
+ y: zod.z.number().finite()
19
+ }),
20
+ zod.z.object({
21
+ name: NameZ,
22
+ kind: zod.z.literal("midpoint"),
23
+ p1: NameZ,
24
+ p2: NameZ
25
+ }),
26
+ zod.z.object({
27
+ name: NameZ,
28
+ kind: zod.z.literal("onSegment"),
29
+ segmentId: NameZ,
30
+ t: zod.z.number().min(0).max(1)
31
+ }),
32
+ zod.z.object({
33
+ name: NameZ,
34
+ kind: zod.z.literal("onLine"),
35
+ lineId: NameZ,
36
+ t: zod.z.number().finite()
37
+ }),
38
+ zod.z.object({
39
+ name: NameZ,
40
+ kind: zod.z.literal("onCircle"),
41
+ circleId: NameZ,
42
+ theta: zod.z.number().finite()
43
+ }),
44
+ zod.z.object({
45
+ name: NameZ,
46
+ kind: zod.z.literal("perpFoot"),
47
+ from: NameZ,
48
+ onLine: NameZ
49
+ }),
50
+ zod.z.object({
51
+ name: NameZ,
52
+ kind: zod.z.literal("circumcenter"),
53
+ vertices: zod.z.tuple([NameZ, NameZ, NameZ])
54
+ }),
55
+ zod.z.object({
56
+ name: NameZ,
57
+ kind: zod.z.literal("incenter"),
58
+ vertices: zod.z.tuple([NameZ, NameZ, NameZ])
59
+ }),
60
+ zod.z.object({
61
+ name: NameZ,
62
+ kind: zod.z.literal("centroid"),
63
+ vertices: zod.z.tuple([NameZ, NameZ, NameZ])
64
+ }),
65
+ zod.z.object({
66
+ name: NameZ,
67
+ kind: zod.z.literal("orthocenter"),
68
+ vertices: zod.z.tuple([NameZ, NameZ, NameZ])
69
+ }),
70
+ zod.z.object({
71
+ name: NameZ,
72
+ kind: zod.z.literal("intersection"),
73
+ ref1: NameZ,
74
+ ref2: NameZ,
75
+ branch: zod.z.union([zod.z.literal(0), zod.z.literal(1)]).optional()
76
+ })
77
+ ]);
78
+ var DslShape = zod.z.discriminatedUnion("kind", [
79
+ zod.z.object({
80
+ name: NameZ,
81
+ kind: zod.z.literal("segment"),
82
+ p1: NameZ,
83
+ p2: NameZ
84
+ }),
85
+ zod.z.object({
86
+ name: NameZ,
87
+ kind: zod.z.literal("line"),
88
+ p1: NameZ,
89
+ p2: NameZ
90
+ }),
91
+ zod.z.object({
92
+ name: NameZ,
93
+ kind: zod.z.literal("ray"),
94
+ origin: NameZ,
95
+ through: NameZ
96
+ }),
97
+ zod.z.object({
98
+ name: NameZ,
99
+ kind: zod.z.literal("polygon"),
100
+ vertices: zod.z.array(NameZ).min(3)
101
+ }),
102
+ // Line constructions
103
+ zod.z.object({
104
+ name: NameZ,
105
+ kind: zod.z.literal("perpendicular"),
106
+ throughPoint: NameZ,
107
+ toLine: NameZ
108
+ }),
109
+ zod.z.object({
110
+ name: NameZ,
111
+ kind: zod.z.literal("parallel"),
112
+ throughPoint: NameZ,
113
+ toLine: NameZ
114
+ }),
115
+ zod.z.object({
116
+ name: NameZ,
117
+ kind: zod.z.literal("perpBisector"),
118
+ p1: NameZ,
119
+ p2: NameZ
120
+ }),
121
+ zod.z.object({
122
+ name: NameZ,
123
+ kind: zod.z.literal("angleBisector"),
124
+ p1: NameZ,
125
+ vertex: NameZ,
126
+ p2: NameZ
127
+ }),
128
+ zod.z.object({
129
+ name: NameZ,
130
+ kind: zod.z.literal("tangent"),
131
+ throughPoint: NameZ,
132
+ toCircle: NameZ,
133
+ branch: zod.z.union([zod.z.literal(0), zod.z.literal(1), zod.z.literal("on")]).optional()
134
+ }),
135
+ // Circle constructions
136
+ zod.z.object({
137
+ name: NameZ,
138
+ kind: zod.z.literal("circleCP"),
139
+ center: NameZ,
140
+ surfacePoint: NameZ
141
+ }),
142
+ zod.z.object({
143
+ name: NameZ,
144
+ kind: zod.z.literal("circle3"),
145
+ p1: NameZ,
146
+ p2: NameZ,
147
+ p3: NameZ
148
+ })
149
+ ]);
150
+ var DslInput = zod.z.object({
151
+ version: zod.z.literal(1),
152
+ points: zod.z.array(DslPoint),
153
+ shapes: zod.z.array(DslShape).default([])
154
+ });
155
+
156
+ // src/core/scene/types.ts
157
+ var DEFAULT_VIEW_2D = {
158
+ bbox: [-10, 10, 10, -10],
159
+ showAxis: false,
160
+ showGrid: false
161
+ };
162
+ var DEFAULT_VIEW_3D = {
163
+ bbox3D: [-5, 5, -5, 5, -5, 5],
164
+ azimuth: 60,
165
+ elevation: 30
166
+ };
167
+ var DEFAULT_VIEW_GRAPH2D = {
168
+ xMin: -10,
169
+ xMax: 10,
170
+ yMin: -10,
171
+ yMax: 10,
172
+ showAxis: true,
173
+ showGrid: true
174
+ };
175
+ function createEmptyState(domain) {
176
+ const base = { objects: {}, order: [], counter: 0 };
177
+ switch (domain) {
178
+ case "2d":
179
+ return { ...base, meta: { domain: "2d", version: 1, view: DEFAULT_VIEW_2D } };
180
+ case "3d":
181
+ return { ...base, meta: { domain: "3d", version: 1, view: DEFAULT_VIEW_3D } };
182
+ case "graph2d":
183
+ return { ...base, meta: { domain: "graph2d", version: 1, view: DEFAULT_VIEW_GRAPH2D } };
184
+ }
185
+ }
186
+
187
+ // src/stamps/geometry-2d/dsl/transpile/errors.ts
188
+ function mkError(code, message, opts) {
189
+ return { code, message, path: opts?.path, hint: opts?.hint };
190
+ }
191
+
192
+ // src/stamps/geometry-2d/dsl/transpile/symbols.ts
193
+ function buildSymbols(dsl) {
194
+ const symbols = /* @__PURE__ */ new Map();
195
+ const errors = [];
196
+ let counter = 0;
197
+ for (const p of dsl.points) {
198
+ if (symbols.has(p.name)) {
199
+ errors.push(mkError("DUPLICATE_NAME", `T\xEAn tr\xF9ng: "${p.name}"`, { path: [p.name] }));
200
+ continue;
201
+ }
202
+ symbols.set(p.name, { name: p.name, role: "point", entity: p, index: counter++ });
203
+ }
204
+ for (const s of dsl.shapes) {
205
+ if (symbols.has(s.name)) {
206
+ errors.push(mkError("DUPLICATE_NAME", `T\xEAn tr\xF9ng: "${s.name}"`, { path: [s.name] }));
207
+ continue;
208
+ }
209
+ symbols.set(s.name, { name: s.name, role: "shape", entity: s, index: counter++ });
210
+ }
211
+ return { symbols, errors };
212
+ }
213
+
214
+ // src/stamps/geometry-2d/dsl/transpile/refs.ts
215
+ function isPointLike(sym) {
216
+ return !!sym && sym.role === "point";
217
+ }
218
+ var LINE_LIKE_SHAPE_KINDS = /* @__PURE__ */ new Set([
219
+ "line",
220
+ "segment",
221
+ "ray",
222
+ "perpendicular",
223
+ "parallel",
224
+ "perpBisector",
225
+ "angleBisector",
226
+ "tangent"
227
+ ]);
228
+ var CIRCLE_KINDS = /* @__PURE__ */ new Set(["circleCP", "circle3"]);
229
+ function isLineLike(sym) {
230
+ if (!sym || sym.role !== "shape") return false;
231
+ return LINE_LIKE_SHAPE_KINDS.has(sym.entity.kind);
232
+ }
233
+ function isCircleLike(sym) {
234
+ if (!sym || sym.role !== "shape") return false;
235
+ return CIRCLE_KINDS.has(sym.entity.kind);
236
+ }
237
+ function isSegmentExact(sym) {
238
+ return !!sym && sym.role === "shape" && sym.entity.kind === "segment";
239
+ }
240
+ function validateRefs(dsl, symbols) {
241
+ const errors = [];
242
+ const check = (owner, field, refName, predicate, expected) => {
243
+ const sym = symbols.get(refName);
244
+ if (!sym) {
245
+ errors.push(mkError(
246
+ "UNKNOWN_REF",
247
+ `${owner}.${field} tham chi\u1EBFu "${refName}" kh\xF4ng t\u1ED3n t\u1EA1i`,
248
+ { path: [owner, field] }
249
+ ));
250
+ return;
251
+ }
252
+ if (!predicate(sym)) {
253
+ errors.push(mkError(
254
+ "KIND_MISMATCH",
255
+ `${owner}.${field}="${refName}" sai ki\u1EC3u (c\u1EA7n ${expected}, g\u1EB7p ${sym.role === "point" ? "point" : sym.entity.kind})`,
256
+ { path: [owner, field] }
257
+ ));
258
+ }
259
+ };
260
+ for (const p of dsl.points) {
261
+ switch (p.kind) {
262
+ case "free":
263
+ break;
264
+ case "midpoint":
265
+ check(p.name, "p1", p.p1, isPointLike, "point");
266
+ check(p.name, "p2", p.p2, isPointLike, "point");
267
+ break;
268
+ case "onSegment":
269
+ check(p.name, "segmentId", p.segmentId, isSegmentExact, "segment");
270
+ break;
271
+ case "onLine":
272
+ check(p.name, "lineId", p.lineId, isLineLike, "line-like");
273
+ break;
274
+ case "onCircle":
275
+ check(p.name, "circleId", p.circleId, isCircleLike, "circle");
276
+ break;
277
+ case "perpFoot":
278
+ check(p.name, "from", p.from, isPointLike, "point");
279
+ check(p.name, "onLine", p.onLine, isLineLike, "line-like");
280
+ break;
281
+ case "circumcenter":
282
+ case "incenter":
283
+ case "centroid":
284
+ case "orthocenter":
285
+ for (let i = 0; i < 3; i++) {
286
+ check(p.name, `vertices[${i}]`, p.vertices[i], isPointLike, "point");
287
+ }
288
+ break;
289
+ case "intersection": {
290
+ const refPredicate = (s) => isLineLike(s) || isCircleLike(s);
291
+ check(p.name, "ref1", p.ref1, refPredicate, "line-like ho\u1EB7c circle");
292
+ check(p.name, "ref2", p.ref2, refPredicate, "line-like ho\u1EB7c circle");
293
+ break;
294
+ }
295
+ }
296
+ }
297
+ for (const s of dsl.shapes) {
298
+ switch (s.kind) {
299
+ case "segment":
300
+ case "line":
301
+ check(s.name, "p1", s.p1, isPointLike, "point");
302
+ check(s.name, "p2", s.p2, isPointLike, "point");
303
+ break;
304
+ case "ray":
305
+ check(s.name, "origin", s.origin, isPointLike, "point");
306
+ check(s.name, "through", s.through, isPointLike, "point");
307
+ break;
308
+ case "polygon":
309
+ s.vertices.forEach((v, i) => check(s.name, `vertices[${i}]`, v, isPointLike, "point"));
310
+ break;
311
+ case "perpendicular":
312
+ case "parallel":
313
+ check(s.name, "throughPoint", s.throughPoint, isPointLike, "point");
314
+ check(s.name, "toLine", s.toLine, isLineLike, "line-like");
315
+ break;
316
+ case "perpBisector":
317
+ check(s.name, "p1", s.p1, isPointLike, "point");
318
+ check(s.name, "p2", s.p2, isPointLike, "point");
319
+ break;
320
+ case "angleBisector":
321
+ check(s.name, "p1", s.p1, isPointLike, "point");
322
+ check(s.name, "vertex", s.vertex, isPointLike, "point");
323
+ check(s.name, "p2", s.p2, isPointLike, "point");
324
+ break;
325
+ case "tangent":
326
+ check(s.name, "throughPoint", s.throughPoint, isPointLike, "point");
327
+ check(s.name, "toCircle", s.toCircle, isCircleLike, "circle");
328
+ break;
329
+ case "circleCP":
330
+ check(s.name, "center", s.center, isPointLike, "point");
331
+ check(s.name, "surfacePoint", s.surfacePoint, isPointLike, "point");
332
+ break;
333
+ case "circle3":
334
+ check(s.name, "p1", s.p1, isPointLike, "point");
335
+ check(s.name, "p2", s.p2, isPointLike, "point");
336
+ check(s.name, "p3", s.p3, isPointLike, "point");
337
+ break;
338
+ }
339
+ }
340
+ return { errors };
341
+ }
342
+ function collectRefs(entity) {
343
+ if ("kind" in entity) {
344
+ switch (entity.kind) {
345
+ case "free":
346
+ return [];
347
+ case "midpoint":
348
+ return [entity.p1, entity.p2];
349
+ case "onSegment":
350
+ return [entity.segmentId];
351
+ case "onLine":
352
+ return [entity.lineId];
353
+ case "onCircle":
354
+ return [entity.circleId];
355
+ case "perpFoot":
356
+ return [entity.from, entity.onLine];
357
+ case "circumcenter":
358
+ case "incenter":
359
+ case "centroid":
360
+ case "orthocenter":
361
+ return [...entity.vertices];
362
+ case "intersection":
363
+ return [entity.ref1, entity.ref2];
364
+ case "segment":
365
+ case "line":
366
+ return [entity.p1, entity.p2];
367
+ case "ray":
368
+ return [entity.origin, entity.through];
369
+ case "polygon":
370
+ return [...entity.vertices];
371
+ case "perpendicular":
372
+ case "parallel":
373
+ return [entity.throughPoint, entity.toLine];
374
+ case "perpBisector":
375
+ return [entity.p1, entity.p2];
376
+ case "angleBisector":
377
+ return [entity.p1, entity.vertex, entity.p2];
378
+ case "tangent":
379
+ return [entity.throughPoint, entity.toCircle];
380
+ case "circleCP":
381
+ return [entity.center, entity.surfacePoint];
382
+ case "circle3":
383
+ return [entity.p1, entity.p2, entity.p3];
384
+ }
385
+ }
386
+ return [];
387
+ }
388
+
389
+ // src/stamps/geometry-2d/dsl/transpile/cycles.ts
390
+ function detectCycles(symbols) {
391
+ const color = /* @__PURE__ */ new Map();
392
+ const parent = /* @__PURE__ */ new Map();
393
+ const errors = [];
394
+ const reportedCycles = /* @__PURE__ */ new Set();
395
+ for (const name of symbols.keys()) color.set(name, "white");
396
+ function reportCycle(start, hit) {
397
+ const chain = [start];
398
+ let cur = parent.get(start);
399
+ while (cur && cur !== hit && chain.length < symbols.size + 2) {
400
+ chain.push(cur);
401
+ cur = parent.get(cur);
402
+ }
403
+ chain.push(hit);
404
+ const minIdx = chain.indexOf(chain.reduce((a, b) => a < b ? a : b));
405
+ const rotated = [...chain.slice(minIdx), ...chain.slice(0, minIdx)];
406
+ const key = rotated.join("\u2192");
407
+ if (reportedCycles.has(key)) return;
408
+ reportedCycles.add(key);
409
+ errors.push(mkError(
410
+ "CYCLE",
411
+ `Ph\u1EE5 thu\u1ED9c v\xF2ng: ${chain.reverse().join(" \u2192 ")}`,
412
+ { path: [...chain], hint: "Ki\u1EC3m tra l\u1EA1i quan h\u1EC7 midpoint/perpFoot/intersection." }
413
+ ));
414
+ }
415
+ function dfs(name) {
416
+ color.set(name, "gray");
417
+ const sym = symbols.get(name);
418
+ if (sym) {
419
+ for (const ref of collectRefs(sym.entity)) {
420
+ if (!symbols.has(ref)) continue;
421
+ const c = color.get(ref);
422
+ if (c === "gray") {
423
+ reportCycle(name, ref);
424
+ continue;
425
+ }
426
+ if (c === "white") {
427
+ parent.set(ref, name);
428
+ dfs(ref);
429
+ }
430
+ }
431
+ }
432
+ color.set(name, "black");
433
+ }
434
+ for (const name of symbols.keys()) {
435
+ if (color.get(name) === "white") {
436
+ parent.set(name, null);
437
+ dfs(name);
438
+ }
439
+ }
440
+ return { errors };
441
+ }
442
+
443
+ // src/stamps/geometry-2d/dsl/transpile/ids.ts
444
+ function prefixFor(sym) {
445
+ if (sym.role === "point") {
446
+ const p = sym.entity;
447
+ return p.kind === "intersection" ? "i" : "p";
448
+ }
449
+ const s = sym.entity;
450
+ switch (s.kind) {
451
+ case "segment":
452
+ return "s";
453
+ case "ray":
454
+ return "r";
455
+ case "polygon":
456
+ return "poly";
457
+ case "circleCP":
458
+ case "circle3":
459
+ return "c";
460
+ // line + 5 line-constructions all share 'l'
461
+ case "line":
462
+ case "perpendicular":
463
+ case "parallel":
464
+ case "perpBisector":
465
+ case "angleBisector":
466
+ case "tangent":
467
+ return "l";
468
+ }
469
+ }
470
+ function assignIds(symbols) {
471
+ const counters = { p: 0, i: 0, s: 0, l: 0, r: 0, poly: 0, c: 0 };
472
+ const ids = /* @__PURE__ */ new Map();
473
+ for (const [name, sym] of symbols.entries()) {
474
+ const prefix = prefixFor(sym);
475
+ counters[prefix] += 1;
476
+ ids.set(name, `${prefix}${counters[prefix]}`);
477
+ }
478
+ return ids;
479
+ }
480
+
481
+ // src/stamps/geometry-2d/dsl/transpile/emitPoint.ts
482
+ function resolveId(ids, name) {
483
+ const id = ids.get(name);
484
+ if (!id) throw new Error(`emitPoint: id not assigned for "${name}"`);
485
+ return id;
486
+ }
487
+ function emitPoint(p, ids, kindHints) {
488
+ const baseId = resolveId(ids, p.name);
489
+ const baseFields = {
490
+ label: p.name,
491
+ visible: true,
492
+ locked: false,
493
+ layer: "default",
494
+ schemaVersion: 1
495
+ };
496
+ if (p.kind === "intersection") {
497
+ const r1Hint = kindHints.get(p.ref1);
498
+ const r2Hint = kindHints.get(p.ref2);
499
+ const r1IsCircle = r1Hint === "circle";
500
+ const r2IsCircle = r2Hint === "circle";
501
+ let intersectKind;
502
+ if (r1IsCircle && r2IsCircle) intersectKind = "circleCircle";
503
+ else if (r1IsCircle || r2IsCircle) intersectKind = "lineCircle";
504
+ else intersectKind = "lineLine";
505
+ const attrs = {
506
+ kind: intersectKind,
507
+ ref1: resolveId(ids, p.ref1),
508
+ ref2: resolveId(ids, p.ref2)
509
+ };
510
+ if (intersectKind !== "lineLine") {
511
+ attrs.branch = p.branch ?? 0;
512
+ }
513
+ return {
514
+ id: baseId,
515
+ kind: "intersection",
516
+ ...baseFields,
517
+ attrs
518
+ };
519
+ }
520
+ let constraint;
521
+ switch (p.kind) {
522
+ case "free":
523
+ constraint = { kind: "free", x: p.x, y: p.y };
524
+ break;
525
+ case "midpoint":
526
+ constraint = { kind: "midpoint", p1: resolveId(ids, p.p1), p2: resolveId(ids, p.p2) };
527
+ break;
528
+ case "onSegment":
529
+ constraint = { kind: "onSegment", segmentId: resolveId(ids, p.segmentId), t: p.t };
530
+ break;
531
+ case "onLine":
532
+ constraint = { kind: "onLine", lineId: resolveId(ids, p.lineId), t: p.t };
533
+ break;
534
+ case "onCircle":
535
+ constraint = { kind: "onCircle", circleId: resolveId(ids, p.circleId), theta: p.theta };
536
+ break;
537
+ case "perpFoot":
538
+ constraint = { kind: "perpFoot", from: resolveId(ids, p.from), onLine: resolveId(ids, p.onLine) };
539
+ break;
540
+ case "circumcenter":
541
+ case "incenter":
542
+ case "centroid":
543
+ case "orthocenter":
544
+ constraint = {
545
+ kind: p.kind,
546
+ vertices: [resolveId(ids, p.vertices[0]), resolveId(ids, p.vertices[1]), resolveId(ids, p.vertices[2])]
547
+ };
548
+ break;
549
+ }
550
+ return {
551
+ id: baseId,
552
+ kind: "point",
553
+ ...baseFields,
554
+ attrs: { constraint }
555
+ };
556
+ }
557
+
558
+ // src/stamps/geometry-2d/dsl/transpile/emitShape.ts
559
+ function r(ids, name) {
560
+ const id = ids.get(name);
561
+ if (!id) throw new Error(`emitShape: id not assigned for "${name}"`);
562
+ return id;
563
+ }
564
+ function emitShape(s, ids) {
565
+ const id = r(ids, s.name);
566
+ const base = {
567
+ label: s.name,
568
+ visible: true,
569
+ locked: false,
570
+ layer: "default",
571
+ schemaVersion: 1
572
+ };
573
+ switch (s.kind) {
574
+ case "segment":
575
+ return { id, kind: "segment", ...base, attrs: { p1: r(ids, s.p1), p2: r(ids, s.p2) } };
576
+ case "line":
577
+ return { id, kind: "line", ...base, attrs: { p1: r(ids, s.p1), p2: r(ids, s.p2) } };
578
+ case "ray":
579
+ return { id, kind: "ray", ...base, attrs: { origin: r(ids, s.origin), through: r(ids, s.through) } };
580
+ case "polygon":
581
+ return { id, kind: "polygon", ...base, attrs: { vertices: s.vertices.map((v) => r(ids, v)) } };
582
+ case "perpendicular":
583
+ case "parallel":
584
+ return {
585
+ id,
586
+ kind: "line",
587
+ ...base,
588
+ attrs: { construction: { kind: s.kind, throughPoint: r(ids, s.throughPoint), toLine: r(ids, s.toLine) } }
589
+ };
590
+ case "perpBisector":
591
+ return {
592
+ id,
593
+ kind: "line",
594
+ ...base,
595
+ attrs: { construction: { kind: "perpBisector", p1: r(ids, s.p1), p2: r(ids, s.p2) } }
596
+ };
597
+ case "angleBisector":
598
+ return {
599
+ id,
600
+ kind: "line",
601
+ ...base,
602
+ attrs: { construction: { kind: "angleBisector", p1: r(ids, s.p1), vertex: r(ids, s.vertex), p2: r(ids, s.p2) } }
603
+ };
604
+ case "tangent": {
605
+ const construction = {
606
+ kind: "tangent",
607
+ throughPoint: r(ids, s.throughPoint),
608
+ toCircle: r(ids, s.toCircle)
609
+ };
610
+ if (s.branch !== void 0) construction.branch = s.branch;
611
+ return { id, kind: "line", ...base, attrs: { construction } };
612
+ }
613
+ case "circleCP":
614
+ return {
615
+ id,
616
+ kind: "circle",
617
+ ...base,
618
+ attrs: { center: r(ids, s.center), surfacePoint: r(ids, s.surfacePoint) }
619
+ };
620
+ case "circle3":
621
+ return {
622
+ id,
623
+ kind: "circle",
624
+ ...base,
625
+ attrs: { construction: { kind: "circumscribed", p1: r(ids, s.p1), p2: r(ids, s.p2), p3: r(ids, s.p3) } }
626
+ };
627
+ }
628
+ }
629
+
630
+ // src/stamps/geometry-2d/dsl/transpile.ts
631
+ function hintOf(entity) {
632
+ if ("kind" in entity) {
633
+ switch (entity.kind) {
634
+ case "free":
635
+ case "midpoint":
636
+ case "onSegment":
637
+ case "onLine":
638
+ case "onCircle":
639
+ case "perpFoot":
640
+ case "circumcenter":
641
+ case "incenter":
642
+ case "centroid":
643
+ case "orthocenter":
644
+ case "intersection":
645
+ return "point";
646
+ case "segment":
647
+ return "segment";
648
+ case "line":
649
+ return "line";
650
+ case "ray":
651
+ return "ray";
652
+ case "polygon":
653
+ return "point";
654
+ // not used as ref target in MVP
655
+ case "perpendicular":
656
+ case "parallel":
657
+ case "perpBisector":
658
+ case "angleBisector":
659
+ case "tangent":
660
+ return "lineConstruction";
661
+ case "circleCP":
662
+ case "circle3":
663
+ return "circle";
664
+ }
665
+ }
666
+ return "point";
667
+ }
668
+ function transpile(dslRaw) {
669
+ const parsed = DslInput.safeParse(dslRaw);
670
+ if (!parsed.success) {
671
+ const errors = parsed.error.issues.map(
672
+ (iss) => mkError("SCHEMA", iss.message, { path: iss.path.map(String) })
673
+ );
674
+ return { ok: false, errors };
675
+ }
676
+ const dsl = parsed.data;
677
+ const { symbols, errors: dupErrors } = buildSymbols(dsl);
678
+ const { errors: refErrors } = validateRefs(dsl, symbols);
679
+ const { errors: cycleErrors } = detectCycles(symbols);
680
+ const allErrors = [...dupErrors, ...refErrors, ...cycleErrors];
681
+ if (allErrors.length > 0) return { ok: false, errors: allErrors };
682
+ const ids = assignIds(symbols);
683
+ const kindHints = /* @__PURE__ */ new Map();
684
+ for (const [name, sym] of symbols.entries()) {
685
+ kindHints.set(name, hintOf(sym.entity));
686
+ }
687
+ const objects = {};
688
+ const order = [];
689
+ for (const p of dsl.points) {
690
+ const obj = emitPoint(p, ids, kindHints);
691
+ objects[obj.id] = obj;
692
+ order.push(obj.id);
693
+ }
694
+ for (const s of dsl.shapes) {
695
+ const obj = emitShape(s, ids);
696
+ objects[obj.id] = obj;
697
+ order.push(obj.id);
698
+ }
699
+ const empty = createEmptyState("2d");
700
+ const state = {
701
+ objects,
702
+ order,
703
+ counter: order.length,
704
+ meta: empty.meta
705
+ };
706
+ return { ok: true, state };
707
+ }
708
+ async function callProvider(args) {
709
+ const client = new Anthropic__default.default({ apiKey: args.apiKey });
710
+ const resp = await client.messages.create(
711
+ {
712
+ model: args.model,
713
+ max_tokens: args.maxTokens,
714
+ system: args.system,
715
+ tools: args.tools,
716
+ tool_choice: args.toolChoice,
717
+ messages: args.messages
718
+ },
719
+ args.signal ? { signal: args.signal } : void 0
720
+ );
721
+ return resp;
722
+ }
723
+
724
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-equilateral.ts
725
+ var fixture = {
726
+ problem: "Cho tam gi\xE1c \u0111\u1EC1u ABC c\u1EA1nh 4.",
727
+ dsl: {
728
+ version: 1,
729
+ points: [
730
+ { name: "A", kind: "free", x: 0, y: 0 },
731
+ { name: "B", kind: "free", x: 4, y: 0 },
732
+ { name: "C", kind: "free", x: 2, y: 3.464 }
733
+ ],
734
+ shapes: [
735
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
736
+ ]
737
+ }
738
+ };
739
+
740
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-median.ts
741
+ var fixture2 = {
742
+ problem: "Tam gi\xE1c ABC, M l\xE0 trung \u0111i\u1EC3m BC. V\u1EBD AM.",
743
+ dsl: {
744
+ version: 1,
745
+ points: [
746
+ { name: "A", kind: "free", x: 0, y: 3 },
747
+ { name: "B", kind: "free", x: -2, y: 0 },
748
+ { name: "C", kind: "free", x: 3, y: 0 },
749
+ { name: "M", kind: "midpoint", p1: "B", p2: "C" }
750
+ ],
751
+ shapes: [
752
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
753
+ { name: "AM", kind: "segment", p1: "A", p2: "M" }
754
+ ]
755
+ }
756
+ };
757
+
758
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-altitude.ts
759
+ var fixture3 = {
760
+ problem: "Tam gi\xE1c ABC, AH l\xE0 \u0111\u01B0\u1EDDng cao xu\u1ED1ng BC.",
761
+ dsl: {
762
+ version: 1,
763
+ points: [
764
+ { name: "A", kind: "free", x: 1, y: 3 },
765
+ { name: "B", kind: "free", x: -2, y: 0 },
766
+ { name: "C", kind: "free", x: 3, y: 0 },
767
+ { name: "H", kind: "perpFoot", from: "A", onLine: "BC" }
768
+ ],
769
+ shapes: [
770
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
771
+ { name: "BC", kind: "segment", p1: "B", p2: "C" },
772
+ { name: "AH", kind: "segment", p1: "A", p2: "H" }
773
+ ]
774
+ }
775
+ };
776
+
777
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-centroid.ts
778
+ var fixture4 = {
779
+ problem: "Tam gi\xE1c ABC, G l\xE0 tr\u1ECDng t\xE2m.",
780
+ dsl: {
781
+ version: 1,
782
+ points: [
783
+ { name: "A", kind: "free", x: 0, y: 3 },
784
+ { name: "B", kind: "free", x: -2, y: 0 },
785
+ { name: "C", kind: "free", x: 3, y: 0 },
786
+ { name: "G", kind: "centroid", vertices: ["A", "B", "C"] }
787
+ ],
788
+ shapes: [
789
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
790
+ ]
791
+ }
792
+ };
793
+
794
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-orthocenter.ts
795
+ var fixture5 = {
796
+ problem: "Tam gi\xE1c ABC, H l\xE0 tr\u1EF1c t\xE2m.",
797
+ dsl: {
798
+ version: 1,
799
+ points: [
800
+ { name: "A", kind: "free", x: 0, y: 3 },
801
+ { name: "B", kind: "free", x: -2, y: 0 },
802
+ { name: "C", kind: "free", x: 3, y: 0 },
803
+ { name: "H", kind: "orthocenter", vertices: ["A", "B", "C"] }
804
+ ],
805
+ shapes: [
806
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] }
807
+ ]
808
+ }
809
+ };
810
+
811
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-circumcircle.ts
812
+ var fixture6 = {
813
+ problem: "Tam gi\xE1c ABC n\u1ED9i ti\u1EBFp \u0111\u01B0\u1EDDng tr\xF2n t\xE2m O.",
814
+ dsl: {
815
+ version: 1,
816
+ points: [
817
+ { name: "A", kind: "free", x: 0, y: 3 },
818
+ { name: "B", kind: "free", x: -2, y: 0 },
819
+ { name: "C", kind: "free", x: 3, y: 0 },
820
+ { name: "O", kind: "circumcenter", vertices: ["A", "B", "C"] }
821
+ ],
822
+ shapes: [
823
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
824
+ { name: "k", kind: "circle3", p1: "A", p2: "B", p3: "C" }
825
+ ]
826
+ }
827
+ };
828
+
829
+ // src/stamps/geometry-2d/dsl/fixtures/triangle-incircle.ts
830
+ var fixture7 = {
831
+ problem: "Tam gi\xE1c ABC, I l\xE0 t\xE2m n\u1ED9i ti\u1EBFp, \u0111\u01B0\u1EDDng tr\xF2n (I) ti\u1EBFp x\xFAc BC t\u1EA1i D.",
832
+ dsl: {
833
+ version: 1,
834
+ points: [
835
+ { name: "A", kind: "free", x: 0, y: 3 },
836
+ { name: "B", kind: "free", x: -2, y: 0 },
837
+ { name: "C", kind: "free", x: 3, y: 0 },
838
+ { name: "I", kind: "incenter", vertices: ["A", "B", "C"] },
839
+ { name: "D", kind: "perpFoot", from: "I", onLine: "BC" }
840
+ ],
841
+ shapes: [
842
+ { name: "ABC", kind: "polygon", vertices: ["A", "B", "C"] },
843
+ { name: "BC", kind: "segment", p1: "B", p2: "C" },
844
+ { name: "incircle", kind: "circleCP", center: "I", surfacePoint: "D" }
845
+ ]
846
+ }
847
+ };
848
+
849
+ // src/stamps/geometry-2d/dsl/fixtures/parallelogram.ts
850
+ var fixture8 = {
851
+ problem: "H\xECnh b\xECnh h\xE0nh ABCD, hai \u0111\u01B0\u1EDDng ch\xE9o AC, BD c\u1EAFt nhau t\u1EA1i O.",
852
+ dsl: {
853
+ version: 1,
854
+ points: [
855
+ { name: "A", kind: "free", x: 0, y: 0 },
856
+ { name: "B", kind: "free", x: 4, y: 0 },
857
+ { name: "C", kind: "free", x: 5, y: 2 },
858
+ { name: "D", kind: "free", x: 1, y: 2 },
859
+ { name: "O", kind: "intersection", ref1: "AC", ref2: "BD" }
860
+ ],
861
+ shapes: [
862
+ { name: "ABCD", kind: "polygon", vertices: ["A", "B", "C", "D"] },
863
+ { name: "AC", kind: "segment", p1: "A", p2: "C" },
864
+ { name: "BD", kind: "segment", p1: "B", p2: "D" }
865
+ ]
866
+ }
867
+ };
868
+
869
+ // src/stamps/geometry-2d/dsl/fixtures/two-circles-intersect.ts
870
+ var fixture9 = {
871
+ problem: "Hai \u0111\u01B0\u1EDDng tr\xF2n (O\u2081), (O\u2082) c\u1EAFt nhau t\u1EA1i P, Q.",
872
+ dsl: {
873
+ version: 1,
874
+ points: [
875
+ { name: "O1", kind: "free", x: 0, y: 0 },
876
+ { name: "A1", kind: "free", x: 2, y: 0 },
877
+ { name: "O2", kind: "free", x: 3, y: 0 },
878
+ { name: "A2", kind: "free", x: 5, y: 0 },
879
+ { name: "P", kind: "intersection", ref1: "k1", ref2: "k2", branch: 0 },
880
+ { name: "Q", kind: "intersection", ref1: "k1", ref2: "k2", branch: 1 }
881
+ ],
882
+ shapes: [
883
+ { name: "k1", kind: "circleCP", center: "O1", surfacePoint: "A1" },
884
+ { name: "k2", kind: "circleCP", center: "O2", surfacePoint: "A2" }
885
+ ]
886
+ }
887
+ };
888
+
889
+ // src/stamps/geometry-2d/ai/prompt.ts
890
+ var FIXTURES = [fixture, fixture2, fixture3, fixture4, fixture5, fixture6, fixture7, fixture8, fixture9];
891
+ function buildSystemPrompt() {
892
+ const examples = FIXTURES.map(
893
+ (f, i) => `### V\xED d\u1EE5 ${i + 1}
894
+ **\u0110\u1EC1:** ${f.problem}
895
+ **DSL:**
896
+ \`\`\`json
897
+ ${JSON.stringify(f.dsl, null, 2)}
898
+ \`\`\``
899
+ ).join("\n\n");
900
+ return `B\u1EA1n l\xE0 tr\u1EE3 l\xFD v\u1EBD h\xECnh h\u1ECDc 2D cho h\u1ECDc sinh THCS v\xE0 l\u1EDBp 10 Vi\u1EC7t Nam.
901
+
902
+ ## Nhi\u1EC7m v\u1EE5
903
+ \u0110\u1ECDc \u0111\u1EC1 b\xE0i ti\u1EBFng Vi\u1EC7t \u2192 emit DSL JSON m\xF4 t\u1EA3 h\xECnh. H\u1EC7 th\u1ED1ng s\u1EBD render h\xECnh t\u1EEB DSL.
904
+
905
+ ## Quy t\u1EAFc
906
+ 1. D\xF9ng tool \`build_figure\` khi v\u1EBD \u0111\u01B0\u1EE3c. D\xF9ng tool \`refuse\` khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c ho\u1EB7c \u0111\u1EC1 ngo\xE0i ph\u1EA1m vi (3D, l\u01B0\u1EE3ng gi\xE1c, ph\xE9p bi\u1EBFn h\xECnh l\u1EDBp 11+, \u0111\u1EA1i s\u1ED1).
907
+ 2. \u01AFu ti\xEAn derived points (midpoint, perpFoot, circumcenter, ...) thay v\xEC t\u1EF1 compute to\u1EA1 \u0111\u1ED9.
908
+ 3. Anchor (free) ch\u1EC9 d\xF9ng cho \u0111i\u1EC3m g\u1ED1c (th\u01B0\u1EDDng A, B, C c\u1EE7a tam gi\xE1c). \u0110\u1EB7t coord h\u1EE3p l\xFD quanh g\u1ED1c (-5..5).
909
+ 4. M\u1ECDi \u0111i\u1EC3m + h\xECnh ph\u1EA3i c\xF3 \`name\` (label "A", "M", "O\u2081", ...). Tham chi\u1EBFu b\u1EB1ng name, kh\xF4ng ph\u1EA3i id.
910
+ 5. Tam gi\xE1c: emit c\u1EA3 \`polygon\` (v\u1EBD vi\u1EC1n) + segment/\u0111\u01B0\u1EDDng ph\u1EE5 ri\xEAng n\u1EBFu \u0111\u1EC1 y\xEAu c\u1EA7u (\u0111\u01B0\u1EDDng cao, trung tuy\u1EBFn).
911
+ 6. \u0110\u01B0\u1EDDng tr\xF2n (O; R) cho tr\u01B0\u1EDBc b\xE1n k\xEDnh s\u1ED1: emit anchor helper tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n r\u1ED3i d\xF9ng \`circleCP\` (DSL kh\xF4ng h\u1ED7 tr\u1EE3 radius numeric tr\u1EF1c ti\u1EBFp).
912
+ 7. N\u1EBFu \u0111\u1EC1 m\u01A1 h\u1ED3: ch\u1ECDn case ph\u1ED5 bi\u1EBFn nh\u1EA5t, kh\xF4ng h\u1ECFi l\u1EA1i.
913
+
914
+ ## Primitives s\u1EB5n c\xF3
915
+ **Points:** free, midpoint, onSegment, onLine, onCircle, perpFoot, circumcenter, incenter, centroid, orthocenter, intersection
916
+ **Shapes:** segment, line, ray, polygon, perpendicular, parallel, perpBisector, angleBisector, tangent, circleCP, circle3
917
+
918
+ ## 9 v\xED d\u1EE5
919
+ ${examples}
920
+
921
+ ## Khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c
922
+ G\u1ECDi \`refuse\` v\u1EDBi \`reason\` ti\u1EBFng Vi\u1EC7t gi\u1EA3i th\xEDch c\u1EE5 th\u1EC3 (vd: "\u0110\u1EC1 thu\u1ED9c l\u1EDBp 11, ngo\xE0i ph\u1EA1m vi MVP" ho\u1EB7c "\u0110\u1EC1 kh\xF4ng r\xF5 v\u1ECB tr\xED \u0111i\u1EC3m M").`;
923
+ }
924
+ var BUILD_FIGURE_TOOL = {
925
+ name: "build_figure",
926
+ description: "V\u1EBD h\xECnh h\u1ECDc 2D theo \u0111\u1EC1 b\xE0i. Emit DSL JSON m\xF4 t\u1EA3 c\xE1c \u0111i\u1EC3m v\xE0 h\xECnh.",
927
+ input_schema: zodToJsonSchema.zodToJsonSchema(DslInput, {
928
+ target: "jsonSchema7",
929
+ $refStrategy: "none"
930
+ })
931
+ };
932
+ var RefuseInputZ = zod.z.object({
933
+ reason: zod.z.string().min(1).describe("L\xFD do kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c (ti\u1EBFng Vi\u1EC7t)")
934
+ });
935
+ var REFUSE_TOOL = {
936
+ name: "refuse",
937
+ description: "T\u1EEB ch\u1ED1i khi kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c ho\u1EB7c \u0111\u1EC1 ngo\xE0i ph\u1EA1m vi (3D, l\u01B0\u1EE3ng gi\xE1c, l\u1EDBp 11+).",
938
+ input_schema: zodToJsonSchema.zodToJsonSchema(RefuseInputZ, { target: "jsonSchema7" })
939
+ };
940
+ var TOOLS = [BUILD_FIGURE_TOOL, REFUSE_TOOL];
941
+
942
+ // src/stamps/geometry-2d/ai/buildFigure.ts
943
+ var DEFAULT_MODEL = "claude-opus-4-7";
944
+ var DEFAULT_MAX_TOKENS = 8192;
945
+ function toUsage(u) {
946
+ return {
947
+ inputTokens: u.input_tokens,
948
+ outputTokens: u.output_tokens,
949
+ cacheReadTokens: u.cache_read_input_tokens ?? 0,
950
+ cacheCreationTokens: u.cache_creation_input_tokens ?? 0
951
+ };
952
+ }
953
+ async function generateFigure(problem, opts) {
954
+ if (!opts.apiKey) {
955
+ return { ok: false, reason: "api_error", message: "apiKey b\u1EAFt bu\u1ED9c" };
956
+ }
957
+ if (!problem || !problem.trim()) {
958
+ return { ok: false, reason: "api_error", message: "\u0110\u1EC1 b\xE0i r\u1ED7ng" };
959
+ }
960
+ const systemText = buildSystemPrompt();
961
+ const enableCaching = opts.enableCaching !== false;
962
+ const systemBlock = enableCaching ? { type: "text", text: systemText, cache_control: { type: "ephemeral" } } : { type: "text", text: systemText };
963
+ let response;
964
+ try {
965
+ response = await callProvider({
966
+ apiKey: opts.apiKey,
967
+ model: opts.model ?? DEFAULT_MODEL,
968
+ maxTokens: opts.maxTokens ?? DEFAULT_MAX_TOKENS,
969
+ system: [systemBlock],
970
+ tools: TOOLS,
971
+ toolChoice: { type: "any" },
972
+ messages: [{ role: "user", content: problem }],
973
+ signal: opts.signal
974
+ });
975
+ } catch (e) {
976
+ const err = e;
977
+ return {
978
+ ok: false,
979
+ reason: "api_error",
980
+ message: err.message ?? "L\u1ED7i g\u1ECDi Claude API",
981
+ ...err.status !== void 0 ? { status: err.status } : {}
982
+ };
983
+ }
984
+ const usage = toUsage(response.usage);
985
+ const toolUse = response.content.find((c) => c.type === "tool_use");
986
+ if (!toolUse || toolUse.type !== "tool_use") {
987
+ const text = response.content.find((c) => c.type === "text");
988
+ const textStr = text?.type === "text" ? text.text : "(empty)";
989
+ return {
990
+ ok: false,
991
+ reason: "parse_error",
992
+ message: "AI kh\xF4ng g\u1ECDi tool n\xE0o. Response: " + textStr,
993
+ raw: response.content,
994
+ usage
995
+ };
996
+ }
997
+ if (toolUse.name === "refuse") {
998
+ const input = toolUse.input;
999
+ return {
1000
+ ok: false,
1001
+ reason: "refused",
1002
+ message: input.reason ?? "AI t\u1EEB ch\u1ED1i kh\xF4ng n\xEAu l\xFD do",
1003
+ usage
1004
+ };
1005
+ }
1006
+ if (toolUse.name !== "build_figure") {
1007
+ return {
1008
+ ok: false,
1009
+ reason: "parse_error",
1010
+ message: `Tool kh\xF4ng x\xE1c \u0111\u1ECBnh: "${toolUse.name}"`,
1011
+ raw: toolUse,
1012
+ usage
1013
+ };
1014
+ }
1015
+ const tResult = transpile(toolUse.input);
1016
+ if (!tResult.ok) {
1017
+ return {
1018
+ ok: false,
1019
+ reason: "transpile_error",
1020
+ message: "DSL t\u1EEB AI kh\xF4ng h\u1EE3p l\u1EC7",
1021
+ errors: tResult.errors,
1022
+ dsl: toolUse.input,
1023
+ usage
1024
+ };
1025
+ }
1026
+ return {
1027
+ ok: true,
1028
+ state: tResult.state,
1029
+ dsl: toolUse.input,
1030
+ usage
1031
+ };
1032
+ }
1033
+
1034
+ exports.generateFigure = generateFigure;
1035
+ //# sourceMappingURL=ai.js.map
1036
+ //# sourceMappingURL=ai.js.map